aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/core/ext
diff options
context:
space:
mode:
Diffstat (limited to 'src/core/ext')
-rw-r--r--src/core/ext/lb_policy/grpclb/load_balancer_api.c163
-rw-r--r--src/core/ext/lb_policy/grpclb/load_balancer_api.h85
-rw-r--r--src/core/ext/lb_policy/grpclb/proto/grpc/lb/v0/load_balancer.pb.c119
-rw-r--r--src/core/ext/lb_policy/grpclb/proto/grpc/lb/v0/load_balancer.pb.h182
-rw-r--r--src/core/ext/lb_policy/pick_first/pick_first.c452
-rw-r--r--src/core/ext/lb_policy/round_robin/round_robin.c574
-rw-r--r--src/core/ext/transport/chttp2/transport/chttp2_transport.c57
-rw-r--r--src/core/ext/transport/chttp2/transport/frame_data.c24
-rw-r--r--src/core/ext/transport/chttp2/transport/frame_data.h2
-rw-r--r--src/core/ext/transport/chttp2/transport/frame_rst_stream.c8
-rw-r--r--src/core/ext/transport/chttp2/transport/frame_rst_stream.h4
-rw-r--r--src/core/ext/transport/chttp2/transport/frame_window_update.c12
-rw-r--r--src/core/ext/transport/chttp2/transport/frame_window_update.h4
-rw-r--r--src/core/ext/transport/chttp2/transport/hpack_encoder.c6
-rw-r--r--src/core/ext/transport/chttp2/transport/hpack_encoder.h2
-rw-r--r--src/core/ext/transport/chttp2/transport/hpack_parser.c3
-rw-r--r--src/core/ext/transport/chttp2/transport/internal.h8
-rw-r--r--src/core/ext/transport/chttp2/transport/parsing.c34
-rw-r--r--src/core/ext/transport/chttp2/transport/writing.c42
19 files changed, 1729 insertions, 52 deletions
diff --git a/src/core/ext/lb_policy/grpclb/load_balancer_api.c b/src/core/ext/lb_policy/grpclb/load_balancer_api.c
new file mode 100644
index 0000000000..d8af644870
--- /dev/null
+++ b/src/core/ext/lb_policy/grpclb/load_balancer_api.c
@@ -0,0 +1,163 @@
+/*
+ *
+ * Copyright 2016, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#include "src/core/ext/lb_policy/grpclb/load_balancer_api.h"
+#include "third_party/nanopb/pb_decode.h"
+#include "third_party/nanopb/pb_encode.h"
+
+#include <grpc/support/alloc.h>
+
+typedef struct decode_serverlist_arg {
+ int first_pass;
+ int i;
+ size_t num_servers;
+ grpc_grpclb_server **servers;
+} 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 = *arg;
+ if (dec_arg->first_pass != 0) { /* first pass */
+ grpc_grpclb_server server;
+ if (!pb_decode(stream, grpc_lb_v0_Server_fields, &server)) {
+ return false;
+ }
+ dec_arg->num_servers++;
+ } else { /* second pass */
+ grpc_grpclb_server *server = gpr_malloc(sizeof(grpc_grpclb_server));
+ GPR_ASSERT(dec_arg->num_servers > 0);
+ if (dec_arg->i == 0) { /* first iteration of second pass */
+ dec_arg->servers =
+ gpr_malloc(sizeof(grpc_grpclb_server *) * dec_arg->num_servers);
+ }
+ if (!pb_decode(stream, grpc_lb_v0_Server_fields, server)) {
+ return false;
+ }
+ dec_arg->servers[dec_arg->i++] = server;
+ }
+
+ return true;
+}
+
+grpc_grpclb_request *grpc_grpclb_request_create(const char *lb_service_name) {
+ grpc_grpclb_request *req = gpr_malloc(sizeof(grpc_grpclb_request));
+
+ req->has_client_stats = 0; /* TODO(dgq): add support for stats once defined */
+ req->has_initial_request = 1;
+ req->initial_request.has_name = 1;
+ strncpy(req->initial_request.name, lb_service_name,
+ GRPC_GRPCLB_SERVICE_NAME_MAX_LENGTH);
+ return req;
+}
+
+gpr_slice grpc_grpclb_request_encode(const grpc_grpclb_request *request) {
+ size_t encoded_length;
+ pb_ostream_t sizestream;
+ pb_ostream_t outputstream;
+ gpr_slice slice;
+ memset(&sizestream, 0, sizeof(pb_ostream_t));
+ pb_encode(&sizestream, grpc_lb_v0_LoadBalanceRequest_fields, request);
+ encoded_length = sizestream.bytes_written;
+
+ slice = gpr_slice_malloc(encoded_length);
+ outputstream =
+ pb_ostream_from_buffer(GPR_SLICE_START_PTR(slice), encoded_length);
+ GPR_ASSERT(pb_encode(&outputstream, grpc_lb_v0_LoadBalanceRequest_fields,
+ request) != 0);
+ return slice;
+}
+
+void grpc_grpclb_request_destroy(grpc_grpclb_request *request) {
+ gpr_free(request);
+}
+
+grpc_grpclb_response *grpc_grpclb_response_parse(gpr_slice encoded_response) {
+ bool status;
+ pb_istream_t stream =
+ pb_istream_from_buffer(GPR_SLICE_START_PTR(encoded_response),
+ GPR_SLICE_LENGTH(encoded_response));
+ grpc_grpclb_response *res = gpr_malloc(sizeof(grpc_grpclb_response));
+ memset(res, 0, sizeof(*res));
+ status = pb_decode(&stream, grpc_lb_v0_LoadBalanceResponse_fields, res);
+ GPR_ASSERT(status == true);
+ return res;
+}
+
+grpc_grpclb_serverlist *grpc_grpclb_response_parse_serverlist(
+ gpr_slice encoded_response) {
+ grpc_grpclb_serverlist *sl = gpr_malloc(sizeof(grpc_grpclb_serverlist));
+ bool status;
+ decode_serverlist_arg arg;
+ pb_istream_t stream =
+ pb_istream_from_buffer(GPR_SLICE_START_PTR(encoded_response),
+ GPR_SLICE_LENGTH(encoded_response));
+ pb_istream_t stream_at_start = stream;
+ grpc_grpclb_response *res = gpr_malloc(sizeof(grpc_grpclb_response));
+ memset(res, 0, sizeof(*res));
+ memset(&arg, 0, sizeof(decode_serverlist_arg));
+
+ res->server_list.servers.funcs.decode = decode_serverlist;
+ res->server_list.servers.arg = &arg;
+ arg.first_pass = 1;
+ status = pb_decode(&stream, grpc_lb_v0_LoadBalanceResponse_fields, res);
+ GPR_ASSERT(status == true);
+ GPR_ASSERT(arg.num_servers > 0);
+
+ arg.first_pass = 0;
+ status =
+ pb_decode(&stream_at_start, grpc_lb_v0_LoadBalanceResponse_fields, res);
+ GPR_ASSERT(status == true);
+ GPR_ASSERT(arg.servers != NULL);
+
+ sl->num_servers = arg.num_servers;
+ sl->servers = arg.servers;
+ if (res->server_list.has_expiration_interval) {
+ sl->expiration_interval = res->server_list.expiration_interval;
+ }
+ grpc_grpclb_response_destroy(res);
+ return sl;
+}
+
+void grpc_grpclb_destroy_serverlist(grpc_grpclb_serverlist *serverlist) {
+ size_t i;
+ for (i = 0; i < serverlist->num_servers; i++) {
+ gpr_free(serverlist->servers[i]);
+ }
+ gpr_free(serverlist->servers);
+ gpr_free(serverlist);
+}
+
+void grpc_grpclb_response_destroy(grpc_grpclb_response *response) {
+ gpr_free(response);
+}
diff --git a/src/core/ext/lb_policy/grpclb/load_balancer_api.h b/src/core/ext/lb_policy/grpclb/load_balancer_api.h
new file mode 100644
index 0000000000..d329a2ffe8
--- /dev/null
+++ b/src/core/ext/lb_policy/grpclb/load_balancer_api.h
@@ -0,0 +1,85 @@
+/*
+ *
+ * Copyright 2016, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef GRPC_CORE_EXT_LB_POLICY_GRPCLB_LOAD_BALANCER_API_H
+#define GRPC_CORE_EXT_LB_POLICY_GRPCLB_LOAD_BALANCER_API_H
+
+#include <grpc/support/slice_buffer.h>
+
+#include "src/core/ext/lb_policy/grpclb/proto/grpc/lb/v0/load_balancer.pb.h"
+#include "src/core/lib/client_config/lb_policy_factory.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define GRPC_GRPCLB_SERVICE_NAME_MAX_LENGTH 128
+
+typedef grpc_lb_v0_LoadBalanceRequest grpc_grpclb_request;
+typedef grpc_lb_v0_LoadBalanceResponse grpc_grpclb_response;
+typedef grpc_lb_v0_Server grpc_grpclb_server;
+typedef grpc_lb_v0_Duration grpc_grpclb_duration;
+typedef struct grpc_grpclb_serverlist {
+ grpc_grpclb_server **servers;
+ size_t num_servers;
+ grpc_grpclb_duration expiration_interval;
+} grpc_grpclb_serverlist;
+
+/** Create a request for a gRPC LB service under \a lb_service_name */
+grpc_grpclb_request *grpc_grpclb_request_create(const char *lb_service_name);
+
+/** Protocol Buffers v3-encode \a request */
+gpr_slice grpc_grpclb_request_encode(const grpc_grpclb_request *request);
+
+/** Destroy \a request */
+void grpc_grpclb_request_destroy(grpc_grpclb_request *request);
+
+/** Parse (ie, decode) the bytes in \a encoded_response as a \a
+ * grpc_grpclb_response */
+grpc_grpclb_response *grpc_grpclb_response_parse(gpr_slice encoded_response);
+
+/** Destroy \a serverlist */
+void grpc_grpclb_destroy_serverlist(grpc_grpclb_serverlist *serverlist);
+
+/** Parse the list of servers from an encoded \a grpc_grpclb_response */
+grpc_grpclb_serverlist *grpc_grpclb_response_parse_serverlist(
+ gpr_slice encoded_response);
+
+/** Destroy \a response */
+void grpc_grpclb_response_destroy(grpc_grpclb_response *response);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* GRPC_CORE_EXT_LB_POLICY_GRPCLB_LOAD_BALANCER_API_H */
diff --git a/src/core/ext/lb_policy/grpclb/proto/grpc/lb/v0/load_balancer.pb.c b/src/core/ext/lb_policy/grpclb/proto/grpc/lb/v0/load_balancer.pb.c
new file mode 100644
index 0000000000..9719673181
--- /dev/null
+++ b/src/core/ext/lb_policy/grpclb/proto/grpc/lb/v0/load_balancer.pb.c
@@ -0,0 +1,119 @@
+/*
+ *
+ * Copyright 2016, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+/* Automatically generated nanopb constant definitions */
+/* Generated by nanopb-0.3.5-dev */
+
+#include "src/core/ext/lb_policy/grpclb/proto/grpc/lb/v0/load_balancer.pb.h"
+
+#if PB_PROTO_HEADER_VERSION != 30
+#error Regenerate this file with the current version of nanopb generator.
+#endif
+
+
+
+const pb_field_t grpc_lb_v0_Duration_fields[3] = {
+ PB_FIELD( 1, INT64 , OPTIONAL, STATIC , FIRST, grpc_lb_v0_Duration, seconds, seconds, 0),
+ PB_FIELD( 2, INT32 , OPTIONAL, STATIC , OTHER, grpc_lb_v0_Duration, nanos, seconds, 0),
+ PB_LAST_FIELD
+};
+
+const pb_field_t grpc_lb_v0_LoadBalanceRequest_fields[3] = {
+ PB_FIELD( 1, MESSAGE , OPTIONAL, STATIC , FIRST, grpc_lb_v0_LoadBalanceRequest, initial_request, initial_request, &grpc_lb_v0_InitialLoadBalanceRequest_fields),
+ PB_FIELD( 2, MESSAGE , OPTIONAL, STATIC , OTHER, grpc_lb_v0_LoadBalanceRequest, client_stats, initial_request, &grpc_lb_v0_ClientStats_fields),
+ PB_LAST_FIELD
+};
+
+const pb_field_t grpc_lb_v0_InitialLoadBalanceRequest_fields[2] = {
+ PB_FIELD( 1, STRING , OPTIONAL, STATIC , FIRST, grpc_lb_v0_InitialLoadBalanceRequest, name, name, 0),
+ PB_LAST_FIELD
+};
+
+const pb_field_t grpc_lb_v0_ClientStats_fields[4] = {
+ PB_FIELD( 1, INT64 , OPTIONAL, STATIC , FIRST, grpc_lb_v0_ClientStats, total_requests, total_requests, 0),
+ PB_FIELD( 2, INT64 , OPTIONAL, STATIC , OTHER, grpc_lb_v0_ClientStats, client_rpc_errors, total_requests, 0),
+ PB_FIELD( 3, INT64 , OPTIONAL, STATIC , OTHER, grpc_lb_v0_ClientStats, dropped_requests, client_rpc_errors, 0),
+ PB_LAST_FIELD
+};
+
+const pb_field_t grpc_lb_v0_LoadBalanceResponse_fields[3] = {
+ PB_FIELD( 1, MESSAGE , OPTIONAL, STATIC , FIRST, grpc_lb_v0_LoadBalanceResponse, initial_response, initial_response, &grpc_lb_v0_InitialLoadBalanceResponse_fields),
+ PB_FIELD( 2, MESSAGE , OPTIONAL, STATIC , OTHER, grpc_lb_v0_LoadBalanceResponse, server_list, initial_response, &grpc_lb_v0_ServerList_fields),
+ PB_LAST_FIELD
+};
+
+const pb_field_t grpc_lb_v0_InitialLoadBalanceResponse_fields[4] = {
+ PB_FIELD( 1, STRING , OPTIONAL, STATIC , FIRST, grpc_lb_v0_InitialLoadBalanceResponse, client_config, client_config, 0),
+ PB_FIELD( 2, STRING , OPTIONAL, STATIC , OTHER, grpc_lb_v0_InitialLoadBalanceResponse, load_balancer_delegate, client_config, 0),
+ PB_FIELD( 3, MESSAGE , OPTIONAL, STATIC , OTHER, grpc_lb_v0_InitialLoadBalanceResponse, client_stats_report_interval, load_balancer_delegate, &grpc_lb_v0_Duration_fields),
+ PB_LAST_FIELD
+};
+
+const pb_field_t grpc_lb_v0_ServerList_fields[3] = {
+ PB_FIELD( 1, MESSAGE , REPEATED, CALLBACK, FIRST, grpc_lb_v0_ServerList, servers, servers, &grpc_lb_v0_Server_fields),
+ PB_FIELD( 3, MESSAGE , OPTIONAL, STATIC , OTHER, grpc_lb_v0_ServerList, expiration_interval, servers, &grpc_lb_v0_Duration_fields),
+ PB_LAST_FIELD
+};
+
+const pb_field_t grpc_lb_v0_Server_fields[5] = {
+ PB_FIELD( 1, STRING , OPTIONAL, STATIC , FIRST, grpc_lb_v0_Server, ip_address, ip_address, 0),
+ PB_FIELD( 2, INT32 , OPTIONAL, STATIC , OTHER, grpc_lb_v0_Server, port, ip_address, 0),
+ PB_FIELD( 3, BYTES , OPTIONAL, STATIC , OTHER, grpc_lb_v0_Server, load_balance_token, port, 0),
+ PB_FIELD( 4, BOOL , OPTIONAL, STATIC , OTHER, grpc_lb_v0_Server, drop_request, 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_v0_LoadBalanceRequest, initial_request) < 65536 && pb_membersize(grpc_lb_v0_LoadBalanceRequest, client_stats) < 65536 && pb_membersize(grpc_lb_v0_LoadBalanceResponse, initial_response) < 65536 && pb_membersize(grpc_lb_v0_LoadBalanceResponse, server_list) < 65536 && pb_membersize(grpc_lb_v0_InitialLoadBalanceResponse, client_stats_report_interval) < 65536 && pb_membersize(grpc_lb_v0_ServerList, servers) < 65536 && pb_membersize(grpc_lb_v0_ServerList, expiration_interval) < 65536), YOU_MUST_DEFINE_PB_FIELD_32BIT_FOR_MESSAGES_grpc_lb_v0_Duration_grpc_lb_v0_LoadBalanceRequest_grpc_lb_v0_InitialLoadBalanceRequest_grpc_lb_v0_ClientStats_grpc_lb_v0_LoadBalanceResponse_grpc_lb_v0_InitialLoadBalanceResponse_grpc_lb_v0_ServerList_grpc_lb_v0_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_v0_LoadBalanceRequest, initial_request) < 256 && pb_membersize(grpc_lb_v0_LoadBalanceRequest, client_stats) < 256 && pb_membersize(grpc_lb_v0_LoadBalanceResponse, initial_response) < 256 && pb_membersize(grpc_lb_v0_LoadBalanceResponse, server_list) < 256 && pb_membersize(grpc_lb_v0_InitialLoadBalanceResponse, client_stats_report_interval) < 256 && pb_membersize(grpc_lb_v0_ServerList, servers) < 256 && pb_membersize(grpc_lb_v0_ServerList, expiration_interval) < 256), YOU_MUST_DEFINE_PB_FIELD_16BIT_FOR_MESSAGES_grpc_lb_v0_Duration_grpc_lb_v0_LoadBalanceRequest_grpc_lb_v0_InitialLoadBalanceRequest_grpc_lb_v0_ClientStats_grpc_lb_v0_LoadBalanceResponse_grpc_lb_v0_InitialLoadBalanceResponse_grpc_lb_v0_ServerList_grpc_lb_v0_Server)
+#endif
+
+
diff --git a/src/core/ext/lb_policy/grpclb/proto/grpc/lb/v0/load_balancer.pb.h b/src/core/ext/lb_policy/grpclb/proto/grpc/lb/v0/load_balancer.pb.h
new file mode 100644
index 0000000000..3599f881bb
--- /dev/null
+++ b/src/core/ext/lb_policy/grpclb/proto/grpc/lb/v0/load_balancer.pb.h
@@ -0,0 +1,182 @@
+/*
+ *
+ * Copyright 2016, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+/* Automatically generated nanopb header */
+/* Generated by nanopb-0.3.5-dev */
+
+#ifndef PB_LOAD_BALANCER_PB_H_INCLUDED
+#define PB_LOAD_BALANCER_PB_H_INCLUDED
+#include "third_party/nanopb/pb.h"
+#if PB_PROTO_HEADER_VERSION != 30
+#error Regenerate this file with the current version of nanopb generator.
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Struct definitions */
+typedef struct _grpc_lb_v0_ClientStats {
+ bool has_total_requests;
+ int64_t total_requests;
+ bool has_client_rpc_errors;
+ int64_t client_rpc_errors;
+ bool has_dropped_requests;
+ int64_t dropped_requests;
+} grpc_lb_v0_ClientStats;
+
+typedef struct _grpc_lb_v0_Duration {
+ bool has_seconds;
+ int64_t seconds;
+ bool has_nanos;
+ int32_t nanos;
+} grpc_lb_v0_Duration;
+
+typedef struct _grpc_lb_v0_InitialLoadBalanceRequest {
+ bool has_name;
+ char name[128];
+} grpc_lb_v0_InitialLoadBalanceRequest;
+
+typedef PB_BYTES_ARRAY_T(64) grpc_lb_v0_Server_load_balance_token_t;
+typedef struct _grpc_lb_v0_Server {
+ bool has_ip_address;
+ char ip_address[46];
+ bool has_port;
+ int32_t port;
+ bool has_load_balance_token;
+ grpc_lb_v0_Server_load_balance_token_t load_balance_token;
+ bool has_drop_request;
+ bool drop_request;
+} grpc_lb_v0_Server;
+
+typedef struct _grpc_lb_v0_InitialLoadBalanceResponse {
+ bool has_client_config;
+ char client_config[64];
+ bool has_load_balancer_delegate;
+ char load_balancer_delegate[64];
+ bool has_client_stats_report_interval;
+ grpc_lb_v0_Duration client_stats_report_interval;
+} grpc_lb_v0_InitialLoadBalanceResponse;
+
+typedef struct _grpc_lb_v0_LoadBalanceRequest {
+ bool has_initial_request;
+ grpc_lb_v0_InitialLoadBalanceRequest initial_request;
+ bool has_client_stats;
+ grpc_lb_v0_ClientStats client_stats;
+} grpc_lb_v0_LoadBalanceRequest;
+
+typedef struct _grpc_lb_v0_ServerList {
+ pb_callback_t servers;
+ bool has_expiration_interval;
+ grpc_lb_v0_Duration expiration_interval;
+} grpc_lb_v0_ServerList;
+
+typedef struct _grpc_lb_v0_LoadBalanceResponse {
+ bool has_initial_response;
+ grpc_lb_v0_InitialLoadBalanceResponse initial_response;
+ bool has_server_list;
+ grpc_lb_v0_ServerList server_list;
+} grpc_lb_v0_LoadBalanceResponse;
+
+/* Default values for struct fields */
+
+/* Initializer values for message structs */
+#define grpc_lb_v0_Duration_init_default {false, 0, false, 0}
+#define grpc_lb_v0_LoadBalanceRequest_init_default {false, grpc_lb_v0_InitialLoadBalanceRequest_init_default, false, grpc_lb_v0_ClientStats_init_default}
+#define grpc_lb_v0_InitialLoadBalanceRequest_init_default {false, ""}
+#define grpc_lb_v0_ClientStats_init_default {false, 0, false, 0, false, 0}
+#define grpc_lb_v0_LoadBalanceResponse_init_default {false, grpc_lb_v0_InitialLoadBalanceResponse_init_default, false, grpc_lb_v0_ServerList_init_default}
+#define grpc_lb_v0_InitialLoadBalanceResponse_init_default {false, "", false, "", false, grpc_lb_v0_Duration_init_default}
+#define grpc_lb_v0_ServerList_init_default {{{NULL}, NULL}, false, grpc_lb_v0_Duration_init_default}
+#define grpc_lb_v0_Server_init_default {false, "", false, 0, false, {0, {0}}, false, 0}
+#define grpc_lb_v0_Duration_init_zero {false, 0, false, 0}
+#define grpc_lb_v0_LoadBalanceRequest_init_zero {false, grpc_lb_v0_InitialLoadBalanceRequest_init_zero, false, grpc_lb_v0_ClientStats_init_zero}
+#define grpc_lb_v0_InitialLoadBalanceRequest_init_zero {false, ""}
+#define grpc_lb_v0_ClientStats_init_zero {false, 0, false, 0, false, 0}
+#define grpc_lb_v0_LoadBalanceResponse_init_zero {false, grpc_lb_v0_InitialLoadBalanceResponse_init_zero, false, grpc_lb_v0_ServerList_init_zero}
+#define grpc_lb_v0_InitialLoadBalanceResponse_init_zero {false, "", false, "", false, grpc_lb_v0_Duration_init_zero}
+#define grpc_lb_v0_ServerList_init_zero {{{NULL}, NULL}, false, grpc_lb_v0_Duration_init_zero}
+#define grpc_lb_v0_Server_init_zero {false, "", false, 0, false, {0, {0}}, false, 0}
+
+/* Field tags (for use in manual encoding/decoding) */
+#define grpc_lb_v0_ClientStats_total_requests_tag 1
+#define grpc_lb_v0_ClientStats_client_rpc_errors_tag 2
+#define grpc_lb_v0_ClientStats_dropped_requests_tag 3
+#define grpc_lb_v0_Duration_seconds_tag 1
+#define grpc_lb_v0_Duration_nanos_tag 2
+#define grpc_lb_v0_InitialLoadBalanceRequest_name_tag 1
+#define grpc_lb_v0_Server_ip_address_tag 1
+#define grpc_lb_v0_Server_port_tag 2
+#define grpc_lb_v0_Server_load_balance_token_tag 3
+#define grpc_lb_v0_Server_drop_request_tag 4
+#define grpc_lb_v0_InitialLoadBalanceResponse_client_config_tag 1
+#define grpc_lb_v0_InitialLoadBalanceResponse_load_balancer_delegate_tag 2
+#define grpc_lb_v0_InitialLoadBalanceResponse_client_stats_report_interval_tag 3
+#define grpc_lb_v0_LoadBalanceRequest_initial_request_tag 1
+#define grpc_lb_v0_LoadBalanceRequest_client_stats_tag 2
+#define grpc_lb_v0_ServerList_servers_tag 1
+#define grpc_lb_v0_ServerList_expiration_interval_tag 3
+#define grpc_lb_v0_LoadBalanceResponse_initial_response_tag 1
+#define grpc_lb_v0_LoadBalanceResponse_server_list_tag 2
+
+/* Struct field encoding specification for nanopb */
+extern const pb_field_t grpc_lb_v0_Duration_fields[3];
+extern const pb_field_t grpc_lb_v0_LoadBalanceRequest_fields[3];
+extern const pb_field_t grpc_lb_v0_InitialLoadBalanceRequest_fields[2];
+extern const pb_field_t grpc_lb_v0_ClientStats_fields[4];
+extern const pb_field_t grpc_lb_v0_LoadBalanceResponse_fields[3];
+extern const pb_field_t grpc_lb_v0_InitialLoadBalanceResponse_fields[4];
+extern const pb_field_t grpc_lb_v0_ServerList_fields[3];
+extern const pb_field_t grpc_lb_v0_Server_fields[5];
+
+/* Maximum encoded size of messages (where known) */
+#define grpc_lb_v0_Duration_size 22
+#define grpc_lb_v0_LoadBalanceRequest_size 169
+#define grpc_lb_v0_InitialLoadBalanceRequest_size 131
+#define grpc_lb_v0_ClientStats_size 33
+#define grpc_lb_v0_LoadBalanceResponse_size (165 + grpc_lb_v0_ServerList_size)
+#define grpc_lb_v0_InitialLoadBalanceResponse_size 156
+#define grpc_lb_v0_Server_size 127
+
+/* Message IDs (where set with "msgid" option) */
+#ifdef PB_MSGID
+
+#define LOAD_BALANCER_MESSAGES \
+
+
+#endif
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif
diff --git a/src/core/ext/lb_policy/pick_first/pick_first.c b/src/core/ext/lb_policy/pick_first/pick_first.c
new file mode 100644
index 0000000000..95fe372c1b
--- /dev/null
+++ b/src/core/ext/lb_policy/pick_first/pick_first.c
@@ -0,0 +1,452 @@
+/*
+ *
+ * Copyright 2015-2016, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+#include "src/core/lib/client_config/lb_policy_registry.h"
+#include "src/core/lib/transport/connectivity_state.h"
+
+typedef struct pending_pick {
+ struct pending_pick *next;
+ grpc_pollset *pollset;
+ 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;
+ size_t num_subchannels;
+
+ grpc_closure connectivity_changed;
+
+ /** the selected channel (a grpc_connected_subchannel) */
+ gpr_atm selected;
+
+ /** mutex protecting remaining members */
+ gpr_mu mu;
+ /** have we started picking? */
+ int started_picking;
+ /** are we shut down? */
+ int shutdown;
+ /** 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;
+
+#define GET_SELECTED(p) \
+ ((grpc_connected_subchannel *)gpr_atm_acq_load(&(p)->selected))
+
+static void pf_destroy(grpc_exec_ctx *exec_ctx, grpc_lb_policy *pol) {
+ pick_first_lb_policy *p = (pick_first_lb_policy *)pol;
+ grpc_connected_subchannel *selected = GET_SELECTED(p);
+ size_t i;
+ GPR_ASSERT(p->pending_picks == NULL);
+ for (i = 0; i < p->num_subchannels; i++) {
+ GRPC_SUBCHANNEL_UNREF(exec_ctx, p->subchannels[i], "pick_first");
+ }
+ if (selected != NULL) {
+ GRPC_CONNECTED_SUBCHANNEL_UNREF(exec_ctx, selected, "picked_first");
+ }
+ grpc_connectivity_state_destroy(exec_ctx, &p->state_tracker);
+ gpr_free(p->subchannels);
+ gpr_mu_destroy(&p->mu);
+ gpr_free(p);
+}
+
+static void pf_shutdown(grpc_exec_ctx *exec_ctx, grpc_lb_policy *pol) {
+ pick_first_lb_policy *p = (pick_first_lb_policy *)pol;
+ pending_pick *pp;
+ grpc_connected_subchannel *selected;
+ gpr_mu_lock(&p->mu);
+ selected = GET_SELECTED(p);
+ p->shutdown = 1;
+ pp = p->pending_picks;
+ p->pending_picks = NULL;
+ grpc_connectivity_state_set(exec_ctx, &p->state_tracker,
+ GRPC_CHANNEL_FATAL_FAILURE, "shutdown");
+ /* cancel subscription */
+ if (selected != NULL) {
+ grpc_connected_subchannel_notify_on_state_change(
+ exec_ctx, selected, NULL, NULL, &p->connectivity_changed);
+ } else {
+ grpc_subchannel_notify_on_state_change(
+ exec_ctx, p->subchannels[p->checking_subchannel], NULL, NULL,
+ &p->connectivity_changed);
+ }
+ gpr_mu_unlock(&p->mu);
+ while (pp != NULL) {
+ pending_pick *next = pp->next;
+ *pp->target = NULL;
+ grpc_pollset_set_del_pollset(exec_ctx, p->base.interested_parties,
+ pp->pollset);
+ grpc_exec_ctx_enqueue(exec_ctx, pp->on_complete, true, NULL);
+ gpr_free(pp);
+ pp = next;
+ }
+}
+
+static void pf_cancel_pick(grpc_exec_ctx *exec_ctx, grpc_lb_policy *pol,
+ grpc_connected_subchannel **target) {
+ pick_first_lb_policy *p = (pick_first_lb_policy *)pol;
+ pending_pick *pp;
+ gpr_mu_lock(&p->mu);
+ pp = p->pending_picks;
+ p->pending_picks = NULL;
+ while (pp != NULL) {
+ pending_pick *next = pp->next;
+ if (pp->target == target) {
+ grpc_pollset_set_del_pollset(exec_ctx, p->base.interested_parties,
+ pp->pollset);
+ *target = NULL;
+ grpc_exec_ctx_enqueue(exec_ctx, pp->on_complete, false, NULL);
+ gpr_free(pp);
+ } else {
+ pp->next = p->pending_picks;
+ p->pending_picks = pp;
+ }
+ pp = next;
+ }
+ gpr_mu_unlock(&p->mu);
+}
+
+static void start_picking(grpc_exec_ctx *exec_ctx, pick_first_lb_policy *p) {
+ p->started_picking = 1;
+ 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(grpc_exec_ctx *exec_ctx, grpc_lb_policy *pol) {
+ pick_first_lb_policy *p = (pick_first_lb_policy *)pol;
+ gpr_mu_lock(&p->mu);
+ if (!p->started_picking) {
+ start_picking(exec_ctx, p);
+ }
+ gpr_mu_unlock(&p->mu);
+}
+
+static int pf_pick(grpc_exec_ctx *exec_ctx, grpc_lb_policy *pol,
+ grpc_pollset *pollset, grpc_metadata_batch *initial_metadata,
+ grpc_connected_subchannel **target,
+ grpc_closure *on_complete) {
+ pick_first_lb_policy *p = (pick_first_lb_policy *)pol;
+ pending_pick *pp;
+
+ /* Check atomically for a selected channel */
+ grpc_connected_subchannel *selected = GET_SELECTED(p);
+ if (selected != NULL) {
+ *target = selected;
+ return 1;
+ }
+
+ /* No subchannel selected yet, so acquire lock and then attempt again */
+ gpr_mu_lock(&p->mu);
+ selected = GET_SELECTED(p);
+ if (selected) {
+ gpr_mu_unlock(&p->mu);
+ *target = selected;
+ return 1;
+ } else {
+ if (!p->started_picking) {
+ start_picking(exec_ctx, p);
+ }
+ grpc_pollset_set_add_pollset(exec_ctx, p->base.interested_parties, pollset);
+ pp = gpr_malloc(sizeof(*pp));
+ pp->next = p->pending_picks;
+ pp->pollset = pollset;
+ pp->target = target;
+ pp->on_complete = on_complete;
+ p->pending_picks = pp;
+ gpr_mu_unlock(&p->mu);
+ return 0;
+ }
+}
+
+static void destroy_subchannels(grpc_exec_ctx *exec_ctx, void *arg,
+ bool iomgr_success) {
+ pick_first_lb_policy *p = arg;
+ size_t i;
+ size_t num_subchannels = p->num_subchannels;
+ grpc_subchannel **subchannels;
+
+ gpr_mu_lock(&p->mu);
+ subchannels = p->subchannels;
+ p->num_subchannels = 0;
+ p->subchannels = NULL;
+ gpr_mu_unlock(&p->mu);
+ GRPC_LB_POLICY_WEAK_UNREF(exec_ctx, &p->base, "destroy_subchannels");
+
+ for (i = 0; i < num_subchannels; i++) {
+ GRPC_SUBCHANNEL_UNREF(exec_ctx, subchannels[i], "pick_first");
+ }
+
+ gpr_free(subchannels);
+}
+
+static void pf_connectivity_changed(grpc_exec_ctx *exec_ctx, void *arg,
+ bool iomgr_success) {
+ pick_first_lb_policy *p = arg;
+ grpc_subchannel *selected_subchannel;
+ pending_pick *pp;
+ grpc_connected_subchannel *selected;
+
+ gpr_mu_lock(&p->mu);
+
+ selected = GET_SELECTED(p);
+
+ if (p->shutdown) {
+ gpr_mu_unlock(&p->mu);
+ GRPC_LB_POLICY_WEAK_UNREF(exec_ctx, &p->base, "pick_first_connectivity");
+ return;
+ } else if (selected != NULL) {
+ if (p->checking_connectivity == GRPC_CHANNEL_TRANSIENT_FAILURE) {
+ /* if the selected channel goes bad, we're done */
+ p->checking_connectivity = GRPC_CHANNEL_FATAL_FAILURE;
+ }
+ grpc_connectivity_state_set(exec_ctx, &p->state_tracker,
+ p->checking_connectivity, "selected_changed");
+ if (p->checking_connectivity != GRPC_CHANNEL_FATAL_FAILURE) {
+ grpc_connected_subchannel_notify_on_state_change(
+ exec_ctx, 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_READY:
+ grpc_connectivity_state_set(exec_ctx, &p->state_tracker,
+ GRPC_CHANNEL_READY, "connecting_ready");
+ selected_subchannel = p->subchannels[p->checking_subchannel];
+ selected =
+ grpc_subchannel_get_connected_subchannel(selected_subchannel);
+ GPR_ASSERT(selected != NULL);
+ GRPC_CONNECTED_SUBCHANNEL_REF(selected, "picked_first");
+ /* drop the pick list: we are connected now */
+ GRPC_LB_POLICY_WEAK_REF(&p->base, "destroy_subchannels");
+ gpr_atm_rel_store(&p->selected, (gpr_atm)selected);
+ grpc_exec_ctx_enqueue(
+ exec_ctx, grpc_closure_create(destroy_subchannels, p), true, NULL);
+ /* update any calls that were waiting for a pick */
+ while ((pp = p->pending_picks)) {
+ p->pending_picks = pp->next;
+ *pp->target = selected;
+ grpc_pollset_set_del_pollset(exec_ctx, p->base.interested_parties,
+ pp->pollset);
+ grpc_exec_ctx_enqueue(exec_ctx, pp->on_complete, true, NULL);
+ gpr_free(pp);
+ }
+ grpc_connected_subchannel_notify_on_state_change(
+ exec_ctx, selected, p->base.interested_parties,
+ &p->checking_connectivity, &p->connectivity_changed);
+ break;
+ case GRPC_CHANNEL_TRANSIENT_FAILURE:
+ grpc_connectivity_state_set(exec_ctx, &p->state_tracker,
+ GRPC_CHANNEL_TRANSIENT_FAILURE,
+ "connecting_transient_failure");
+ p->checking_subchannel =
+ (p->checking_subchannel + 1) % p->num_subchannels;
+ p->checking_connectivity = grpc_subchannel_check_connectivity(
+ p->subchannels[p->checking_subchannel]);
+ 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,
+ "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_FATAL_FAILURE:
+ 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_FATAL_FAILURE,
+ "no_more_channels");
+ while ((pp = p->pending_picks)) {
+ p->pending_picks = pp->next;
+ *pp->target = NULL;
+ grpc_exec_ctx_enqueue(exec_ctx, pp->on_complete, true, NULL);
+ 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,
+ "subchannel_failed");
+ p->checking_subchannel %= p->num_subchannels;
+ p->checking_connectivity = grpc_subchannel_check_connectivity(
+ p->subchannels[p->checking_subchannel]);
+ goto loop;
+ }
+ }
+ }
+
+ gpr_mu_unlock(&p->mu);
+}
+
+static grpc_connectivity_state pf_check_connectivity(grpc_exec_ctx *exec_ctx,
+ grpc_lb_policy *pol) {
+ pick_first_lb_policy *p = (pick_first_lb_policy *)pol;
+ grpc_connectivity_state st;
+ gpr_mu_lock(&p->mu);
+ st = grpc_connectivity_state_check(&p->state_tracker);
+ gpr_mu_unlock(&p->mu);
+ return st;
+}
+
+static void pf_notify_on_state_change(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;
+ gpr_mu_lock(&p->mu);
+ grpc_connectivity_state_notify_on_state_change(exec_ctx, &p->state_tracker,
+ current, notify);
+ gpr_mu_unlock(&p->mu);
+}
+
+static void pf_ping_one(grpc_exec_ctx *exec_ctx, grpc_lb_policy *pol,
+ grpc_closure *closure) {
+ pick_first_lb_policy *p = (pick_first_lb_policy *)pol;
+ grpc_connected_subchannel *selected = GET_SELECTED(p);
+ if (selected) {
+ grpc_connected_subchannel_ping(exec_ctx, selected, closure);
+ } else {
+ grpc_exec_ctx_enqueue(exec_ctx, closure, false, NULL);
+ }
+}
+
+static const grpc_lb_policy_vtable pick_first_lb_policy_vtable = {
+ pf_destroy,
+ pf_shutdown,
+ pf_pick,
+ pf_cancel_pick,
+ pf_ping_one,
+ pf_exit_idle,
+ pf_check_connectivity,
+ pf_notify_on_state_change};
+
+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->addresses != NULL);
+ GPR_ASSERT(args->subchannel_factory != NULL);
+
+ if (args->addresses->naddrs == 0) return NULL;
+
+ pick_first_lb_policy *p = gpr_malloc(sizeof(*p));
+ memset(p, 0, sizeof(*p));
+
+ p->subchannels =
+ gpr_malloc(sizeof(grpc_subchannel *) * args->addresses->naddrs);
+ memset(p->subchannels, 0, sizeof(*p->subchannels) * args->addresses->naddrs);
+ grpc_subchannel_args sc_args;
+ size_t subchannel_idx = 0;
+ for (size_t i = 0; i < args->addresses->naddrs; i++) {
+ memset(&sc_args, 0, sizeof(grpc_subchannel_args));
+ sc_args.addr = (struct sockaddr *)(args->addresses->addrs[i].addr);
+ sc_args.addr_len = (size_t)args->addresses->addrs[i].len;
+
+ grpc_subchannel *subchannel = grpc_subchannel_factory_create_subchannel(
+ exec_ctx, args->subchannel_factory, &sc_args);
+
+ if (subchannel != NULL) {
+ p->subchannels[subchannel_idx++] = subchannel;
+ }
+ }
+ if (subchannel_idx == 0) {
+ gpr_free(p->subchannels);
+ gpr_free(p);
+ return NULL;
+ }
+ p->num_subchannels = subchannel_idx;
+
+ grpc_lb_policy_init(&p->base, &pick_first_lb_policy_vtable);
+ grpc_closure_init(&p->connectivity_changed, pf_connectivity_changed, p);
+ gpr_mu_init(&p->mu);
+ 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());
+}
+
+void grpc_lb_policy_pick_first_shutdown() {}
diff --git a/src/core/ext/lb_policy/round_robin/round_robin.c b/src/core/ext/lb_policy/round_robin/round_robin.c
new file mode 100644
index 0000000000..eb6cccdca9
--- /dev/null
+++ b/src/core/ext/lb_policy/round_robin/round_robin.c
@@ -0,0 +1,574 @@
+/*
+ *
+ * Copyright 2015-2016, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+
+#include "src/core/lib/client_config/lb_policy_registry.h"
+#include "src/core/lib/debug/trace.h"
+#include "src/core/lib/transport/connectivity_state.h"
+
+typedef struct round_robin_lb_policy round_robin_lb_policy;
+
+int grpc_lb_round_robin_trace = 0;
+
+/** 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;
+ grpc_pollset *pollset;
+ grpc_connected_subchannel **target;
+ grpc_closure *on_complete;
+} pending_pick;
+
+/** List of subchannels in a connectivity READY state */
+typedef struct ready_list {
+ grpc_subchannel *subchannel;
+ struct ready_list *next;
+ struct ready_list *prev;
+} ready_list;
+
+typedef struct {
+ /** index within policy->subchannels */
+ size_t index;
+ /** backpointer to owning policy */
+ round_robin_lb_policy *policy;
+ /** subchannel itself */
+ grpc_subchannel *subchannel;
+ /** notification that connectivity has changed on subchannel */
+ grpc_closure connectivity_changed_closure;
+ /** this subchannels current position in subchannel->ready_list */
+ ready_list *ready_list_node;
+ /** last observed connectivity */
+ grpc_connectivity_state connectivity_state;
+} subchannel_data;
+
+struct round_robin_lb_policy {
+ /** base policy: must be first */
+ grpc_lb_policy base;
+
+ /** all our subchannels */
+ size_t num_subchannels;
+ subchannel_data **subchannels;
+
+ /** mutex protecting remaining members */
+ gpr_mu mu;
+ /** have we started picking? */
+ int started_picking;
+ /** are we shutting down? */
+ int shutdown;
+ /** List of picks that are waiting on connectivity */
+ pending_pick *pending_picks;
+
+ /** our connectivity state tracker */
+ grpc_connectivity_state_tracker state_tracker;
+
+ /** (Dummy) root of the doubly linked list containing READY subchannels */
+ ready_list ready_list;
+ /** Last pick from the ready list. */
+ ready_list *ready_list_last_pick;
+};
+
+/** Returns the next subchannel from the connected list or NULL if the list is
+ * empty.
+ *
+ * Note that this function does *not* advance p->ready_list_last_pick. Use \a
+ * advance_last_picked_locked() for that. */
+static ready_list *peek_next_connected_locked(const round_robin_lb_policy *p) {
+ ready_list *selected;
+ selected = p->ready_list_last_pick->next;
+
+ while (selected != NULL) {
+ if (selected == &p->ready_list) {
+ GPR_ASSERT(selected->subchannel == NULL);
+ /* skip dummy root */
+ selected = selected->next;
+ } else {
+ GPR_ASSERT(selected->subchannel != NULL);
+ return selected;
+ }
+ }
+ return NULL;
+}
+
+/** Advance the \a ready_list picking head. */
+static void advance_last_picked_locked(round_robin_lb_policy *p) {
+ if (p->ready_list_last_pick->next != NULL) { /* non-empty list */
+ p->ready_list_last_pick = p->ready_list_last_pick->next;
+ if (p->ready_list_last_pick == &p->ready_list) {
+ /* skip dummy root */
+ p->ready_list_last_pick = p->ready_list_last_pick->next;
+ }
+ } else { /* should be an empty list */
+ GPR_ASSERT(p->ready_list_last_pick == &p->ready_list);
+ }
+
+ if (grpc_lb_round_robin_trace) {
+ gpr_log(GPR_DEBUG, "[READYLIST] ADVANCED LAST PICK. NOW AT NODE %p (SC %p)",
+ p->ready_list_last_pick, p->ready_list_last_pick->subchannel);
+ }
+}
+
+/** Prepends (relative to the root at p->ready_list) the connected subchannel \a
+ * csc to the list of ready subchannels. */
+static ready_list *add_connected_sc_locked(round_robin_lb_policy *p,
+ grpc_subchannel *sc) {
+ ready_list *new_elem = gpr_malloc(sizeof(ready_list));
+ new_elem->subchannel = sc;
+ if (p->ready_list.prev == NULL) {
+ /* first element */
+ new_elem->next = &p->ready_list;
+ new_elem->prev = &p->ready_list;
+ p->ready_list.next = new_elem;
+ p->ready_list.prev = new_elem;
+ } else {
+ new_elem->next = &p->ready_list;
+ new_elem->prev = p->ready_list.prev;
+ p->ready_list.prev->next = new_elem;
+ p->ready_list.prev = new_elem;
+ }
+ if (grpc_lb_round_robin_trace) {
+ gpr_log(GPR_DEBUG, "[READYLIST] ADDING NODE %p (SC %p)", new_elem, sc);
+ }
+ return new_elem;
+}
+
+/** Removes \a node from the list of connected subchannels */
+static void remove_disconnected_sc_locked(round_robin_lb_policy *p,
+ ready_list *node) {
+ if (node == NULL) {
+ return;
+ }
+ if (node == p->ready_list_last_pick) {
+ /* If removing the lastly picked node, reset the last pick pointer to the
+ * dummy root of the list */
+ p->ready_list_last_pick = &p->ready_list;
+ }
+
+ /* removing last item */
+ if (node->next == &p->ready_list && node->prev == &p->ready_list) {
+ GPR_ASSERT(p->ready_list.next == node);
+ GPR_ASSERT(p->ready_list.prev == node);
+ p->ready_list.next = NULL;
+ p->ready_list.prev = NULL;
+ } else {
+ node->prev->next = node->next;
+ node->next->prev = node->prev;
+ }
+
+ if (grpc_lb_round_robin_trace) {
+ gpr_log(GPR_DEBUG, "[READYLIST] REMOVED NODE %p (SC %p)", node,
+ node->subchannel);
+ }
+
+ node->next = NULL;
+ node->prev = NULL;
+ node->subchannel = NULL;
+
+ gpr_free(node);
+}
+
+static void rr_destroy(grpc_exec_ctx *exec_ctx, grpc_lb_policy *pol) {
+ round_robin_lb_policy *p = (round_robin_lb_policy *)pol;
+ size_t i;
+ ready_list *elem;
+ for (i = 0; i < p->num_subchannels; i++) {
+ subchannel_data *sd = p->subchannels[i];
+ GRPC_SUBCHANNEL_UNREF(exec_ctx, sd->subchannel, "round_robin");
+ gpr_free(sd);
+ }
+
+ grpc_connectivity_state_destroy(exec_ctx, &p->state_tracker);
+ gpr_free(p->subchannels);
+ gpr_mu_destroy(&p->mu);
+
+ elem = p->ready_list.next;
+ while (elem != NULL && elem != &p->ready_list) {
+ ready_list *tmp;
+ tmp = elem->next;
+ elem->next = NULL;
+ elem->prev = NULL;
+ elem->subchannel = NULL;
+ gpr_free(elem);
+ elem = tmp;
+ }
+ gpr_free(p);
+}
+
+static void rr_shutdown(grpc_exec_ctx *exec_ctx, grpc_lb_policy *pol) {
+ round_robin_lb_policy *p = (round_robin_lb_policy *)pol;
+ pending_pick *pp;
+ size_t i;
+
+ gpr_mu_lock(&p->mu);
+
+ p->shutdown = 1;
+ while ((pp = p->pending_picks)) {
+ p->pending_picks = pp->next;
+ *pp->target = NULL;
+ grpc_exec_ctx_enqueue(exec_ctx, pp->on_complete, false, NULL);
+ gpr_free(pp);
+ }
+ grpc_connectivity_state_set(exec_ctx, &p->state_tracker,
+ GRPC_CHANNEL_FATAL_FAILURE, "shutdown");
+ for (i = 0; i < p->num_subchannels; i++) {
+ subchannel_data *sd = p->subchannels[i];
+ grpc_subchannel_notify_on_state_change(exec_ctx, sd->subchannel, NULL, NULL,
+ &sd->connectivity_changed_closure);
+ }
+ gpr_mu_unlock(&p->mu);
+}
+
+static void rr_cancel_pick(grpc_exec_ctx *exec_ctx, grpc_lb_policy *pol,
+ grpc_connected_subchannel **target) {
+ round_robin_lb_policy *p = (round_robin_lb_policy *)pol;
+ pending_pick *pp;
+ gpr_mu_lock(&p->mu);
+ pp = p->pending_picks;
+ p->pending_picks = NULL;
+ while (pp != NULL) {
+ pending_pick *next = pp->next;
+ if (pp->target == target) {
+ grpc_pollset_set_del_pollset(exec_ctx, p->base.interested_parties,
+ pp->pollset);
+ *target = NULL;
+ grpc_exec_ctx_enqueue(exec_ctx, pp->on_complete, false, NULL);
+ gpr_free(pp);
+ } else {
+ pp->next = p->pending_picks;
+ p->pending_picks = pp;
+ }
+ pp = next;
+ }
+ gpr_mu_unlock(&p->mu);
+}
+
+static void start_picking(grpc_exec_ctx *exec_ctx, round_robin_lb_policy *p) {
+ size_t i;
+ p->started_picking = 1;
+
+ gpr_log(GPR_DEBUG, "LB_POLICY: p=%p num_subchannels=%d", p,
+ p->num_subchannels);
+
+ for (i = 0; i < p->num_subchannels; i++) {
+ subchannel_data *sd = p->subchannels[i];
+ sd->connectivity_state = GRPC_CHANNEL_IDLE;
+ grpc_subchannel_notify_on_state_change(
+ exec_ctx, sd->subchannel, p->base.interested_parties,
+ &sd->connectivity_state, &sd->connectivity_changed_closure);
+ GRPC_LB_POLICY_WEAK_REF(&p->base, "round_robin_connectivity");
+ }
+}
+
+static void rr_exit_idle(grpc_exec_ctx *exec_ctx, grpc_lb_policy *pol) {
+ round_robin_lb_policy *p = (round_robin_lb_policy *)pol;
+ gpr_mu_lock(&p->mu);
+ if (!p->started_picking) {
+ start_picking(exec_ctx, p);
+ }
+ gpr_mu_unlock(&p->mu);
+}
+
+static int rr_pick(grpc_exec_ctx *exec_ctx, grpc_lb_policy *pol,
+ grpc_pollset *pollset, grpc_metadata_batch *initial_metadata,
+ grpc_connected_subchannel **target,
+ grpc_closure *on_complete) {
+ round_robin_lb_policy *p = (round_robin_lb_policy *)pol;
+ pending_pick *pp;
+ ready_list *selected;
+ gpr_mu_lock(&p->mu);
+ if ((selected = peek_next_connected_locked(p))) {
+ gpr_mu_unlock(&p->mu);
+ *target = grpc_subchannel_get_connected_subchannel(selected->subchannel);
+ if (grpc_lb_round_robin_trace) {
+ gpr_log(GPR_DEBUG,
+ "[RR PICK] TARGET <-- CONNECTED SUBCHANNEL %p (NODE %p)",
+ selected->subchannel, selected);
+ }
+ /* only advance the last picked pointer if the selection was used */
+ advance_last_picked_locked(p);
+ return 1;
+ } else {
+ if (!p->started_picking) {
+ start_picking(exec_ctx, p);
+ }
+ grpc_pollset_set_add_pollset(exec_ctx, p->base.interested_parties, pollset);
+ pp = gpr_malloc(sizeof(*pp));
+ pp->next = p->pending_picks;
+ pp->pollset = pollset;
+ pp->target = target;
+ pp->on_complete = on_complete;
+ p->pending_picks = pp;
+ gpr_mu_unlock(&p->mu);
+ return 0;
+ }
+}
+
+static void rr_connectivity_changed(grpc_exec_ctx *exec_ctx, void *arg,
+ bool iomgr_success) {
+ subchannel_data *sd = arg;
+ round_robin_lb_policy *p = sd->policy;
+ pending_pick *pp;
+ ready_list *selected;
+
+ int unref = 0;
+
+ gpr_mu_lock(&p->mu);
+
+ if (p->shutdown) {
+ unref = 1;
+ } else {
+ switch (sd->connectivity_state) {
+ case GRPC_CHANNEL_READY:
+ grpc_connectivity_state_set(exec_ctx, &p->state_tracker,
+ GRPC_CHANNEL_READY, "connecting_ready");
+ /* add the newly connected subchannel to the list of connected ones.
+ * Note that it goes to the "end of the line". */
+ sd->ready_list_node = add_connected_sc_locked(p, sd->subchannel);
+ /* 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. */
+ selected = peek_next_connected_locked(p);
+ if (p->pending_picks != NULL) {
+ /* if the selected subchannel is going to be used for the pending
+ * picks, update the last picked pointer */
+ advance_last_picked_locked(p);
+ }
+ while ((pp = p->pending_picks)) {
+ p->pending_picks = pp->next;
+ *pp->target =
+ grpc_subchannel_get_connected_subchannel(selected->subchannel);
+ if (grpc_lb_round_robin_trace) {
+ gpr_log(GPR_DEBUG,
+ "[RR CONN CHANGED] TARGET <-- SUBCHANNEL %p (NODE %p)",
+ selected->subchannel, selected);
+ }
+ grpc_pollset_set_del_pollset(exec_ctx, p->base.interested_parties,
+ pp->pollset);
+ grpc_exec_ctx_enqueue(exec_ctx, pp->on_complete, true, NULL);
+ gpr_free(pp);
+ }
+ grpc_subchannel_notify_on_state_change(
+ exec_ctx, sd->subchannel, p->base.interested_parties,
+ &sd->connectivity_state, &sd->connectivity_changed_closure);
+ break;
+ case GRPC_CHANNEL_CONNECTING:
+ case GRPC_CHANNEL_IDLE:
+ grpc_connectivity_state_set(exec_ctx, &p->state_tracker,
+ sd->connectivity_state,
+ "connecting_changed");
+ grpc_subchannel_notify_on_state_change(
+ exec_ctx, sd->subchannel, p->base.interested_parties,
+ &sd->connectivity_state, &sd->connectivity_changed_closure);
+ break;
+ case GRPC_CHANNEL_TRANSIENT_FAILURE:
+ /* renew state notification */
+ grpc_subchannel_notify_on_state_change(
+ exec_ctx, sd->subchannel, p->base.interested_parties,
+ &sd->connectivity_state, &sd->connectivity_changed_closure);
+
+ /* remove from ready list if still present */
+ if (sd->ready_list_node != NULL) {
+ remove_disconnected_sc_locked(p, sd->ready_list_node);
+ sd->ready_list_node = NULL;
+ }
+ grpc_connectivity_state_set(exec_ctx, &p->state_tracker,
+ GRPC_CHANNEL_TRANSIENT_FAILURE,
+ "connecting_transient_failure");
+ break;
+ case GRPC_CHANNEL_FATAL_FAILURE:
+ if (sd->ready_list_node != NULL) {
+ remove_disconnected_sc_locked(p, sd->ready_list_node);
+ sd->ready_list_node = NULL;
+ }
+
+ p->num_subchannels--;
+ GPR_SWAP(subchannel_data *, p->subchannels[sd->index],
+ p->subchannels[p->num_subchannels]);
+ GRPC_SUBCHANNEL_UNREF(exec_ctx, sd->subchannel, "round_robin");
+ p->subchannels[sd->index]->index = sd->index;
+ gpr_free(sd);
+
+ unref = 1;
+ if (p->num_subchannels == 0) {
+ grpc_connectivity_state_set(exec_ctx, &p->state_tracker,
+ GRPC_CHANNEL_FATAL_FAILURE,
+ "no_more_channels");
+ while ((pp = p->pending_picks)) {
+ p->pending_picks = pp->next;
+ *pp->target = NULL;
+ grpc_exec_ctx_enqueue(exec_ctx, pp->on_complete, true, NULL);
+ gpr_free(pp);
+ }
+ } else {
+ grpc_connectivity_state_set(exec_ctx, &p->state_tracker,
+ GRPC_CHANNEL_TRANSIENT_FAILURE,
+ "subchannel_failed");
+ }
+ } /* switch */
+ } /* !unref */
+
+ gpr_mu_unlock(&p->mu);
+
+ if (unref) {
+ GRPC_LB_POLICY_WEAK_UNREF(exec_ctx, &p->base, "round_robin_connectivity");
+ }
+}
+
+static grpc_connectivity_state rr_check_connectivity(grpc_exec_ctx *exec_ctx,
+ grpc_lb_policy *pol) {
+ round_robin_lb_policy *p = (round_robin_lb_policy *)pol;
+ grpc_connectivity_state st;
+ gpr_mu_lock(&p->mu);
+ st = grpc_connectivity_state_check(&p->state_tracker);
+ gpr_mu_unlock(&p->mu);
+ return st;
+}
+
+static void rr_notify_on_state_change(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;
+ gpr_mu_lock(&p->mu);
+ grpc_connectivity_state_notify_on_state_change(exec_ctx, &p->state_tracker,
+ current, notify);
+ gpr_mu_unlock(&p->mu);
+}
+
+static void rr_ping_one(grpc_exec_ctx *exec_ctx, grpc_lb_policy *pol,
+ grpc_closure *closure) {
+ round_robin_lb_policy *p = (round_robin_lb_policy *)pol;
+ ready_list *selected;
+ grpc_connected_subchannel *target;
+ gpr_mu_lock(&p->mu);
+ if ((selected = peek_next_connected_locked(p))) {
+ gpr_mu_unlock(&p->mu);
+ target = grpc_subchannel_get_connected_subchannel(selected->subchannel);
+ grpc_connected_subchannel_ping(exec_ctx, target, closure);
+ } else {
+ gpr_mu_unlock(&p->mu);
+ grpc_exec_ctx_enqueue(exec_ctx, closure, false, NULL);
+ }
+}
+
+static const grpc_lb_policy_vtable round_robin_lb_policy_vtable = {
+ rr_destroy,
+ rr_shutdown,
+ rr_pick,
+ rr_cancel_pick,
+ rr_ping_one,
+ rr_exit_idle,
+ rr_check_connectivity,
+ rr_notify_on_state_change};
+
+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 *create_round_robin(grpc_exec_ctx *exec_ctx,
+ grpc_lb_policy_factory *factory,
+ grpc_lb_policy_args *args) {
+ GPR_ASSERT(args->addresses != NULL);
+ GPR_ASSERT(args->subchannel_factory != NULL);
+
+ round_robin_lb_policy *p = gpr_malloc(sizeof(*p));
+ memset(p, 0, sizeof(*p));
+
+ p->subchannels =
+ gpr_malloc(sizeof(*p->subchannels) * args->addresses->naddrs);
+ memset(p->subchannels, 0, sizeof(*p->subchannels) * args->addresses->naddrs);
+
+ grpc_subchannel_args sc_args;
+ size_t subchannel_idx = 0;
+ for (size_t i = 0; i < args->addresses->naddrs; i++) {
+ memset(&sc_args, 0, sizeof(grpc_subchannel_args));
+ sc_args.addr = (struct sockaddr *)(args->addresses->addrs[i].addr);
+ sc_args.addr_len = (size_t)args->addresses->addrs[i].len;
+
+ grpc_subchannel *subchannel = grpc_subchannel_factory_create_subchannel(
+ exec_ctx, args->subchannel_factory, &sc_args);
+
+ if (subchannel != NULL) {
+ subchannel_data *sd = gpr_malloc(sizeof(*sd));
+ memset(sd, 0, sizeof(*sd));
+ p->subchannels[subchannel_idx] = sd;
+ sd->policy = p;
+ sd->index = subchannel_idx;
+ sd->subchannel = subchannel;
+ ++subchannel_idx;
+ grpc_closure_init(&sd->connectivity_changed_closure,
+ rr_connectivity_changed, sd);
+ }
+ }
+ if (subchannel_idx == 0) {
+ gpr_free(p->subchannels);
+ gpr_free(p);
+ return NULL;
+ }
+ p->num_subchannels = subchannel_idx;
+
+ /* The (dummy node) root of the ready list */
+ p->ready_list.subchannel = NULL;
+ p->ready_list.prev = NULL;
+ p->ready_list.next = NULL;
+ p->ready_list_last_pick = &p->ready_list;
+
+ grpc_lb_policy_init(&p->base, &round_robin_lb_policy_vtable);
+ grpc_connectivity_state_init(&p->state_tracker, GRPC_CHANNEL_IDLE,
+ "round_robin");
+ gpr_mu_init(&p->mu);
+ return &p->base;
+}
+
+static const grpc_lb_policy_factory_vtable round_robin_factory_vtable = {
+ round_robin_factory_ref, round_robin_factory_unref, create_round_robin,
+ "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("round_robin", &grpc_lb_round_robin_trace);
+}
+
+void grpc_lb_policy_round_robin_shutdown() {}
diff --git a/src/core/ext/transport/chttp2/transport/chttp2_transport.c b/src/core/ext/transport/chttp2/transport/chttp2_transport.c
index 0a307a73a6..b835e74c56 100644
--- a/src/core/ext/transport/chttp2/transport/chttp2_transport.c
+++ b/src/core/ext/transport/chttp2/transport/chttp2_transport.c
@@ -758,23 +758,35 @@ static void maybe_start_some_streams(
}
}
+#define CLOSURE_BARRIER_STATS_BIT (1 << 0)
+#define CLOSURE_BARRIER_FAILURE_BIT (1 << 1)
+#define CLOSURE_BARRIER_FIRST_REF_BIT (1 << 16)
+
static grpc_closure *add_closure_barrier(grpc_closure *closure) {
- closure->final_data += 2;
+ closure->final_data += CLOSURE_BARRIER_FIRST_REF_BIT;
return closure;
}
void grpc_chttp2_complete_closure_step(grpc_exec_ctx *exec_ctx,
+ grpc_chttp2_stream_global *stream_global,
grpc_closure **pclosure, int success) {
grpc_closure *closure = *pclosure;
if (closure == NULL) {
return;
}
- closure->final_data -= 2;
+ closure->final_data -= CLOSURE_BARRIER_FIRST_REF_BIT;
if (!success) {
- closure->final_data |= 1;
+ closure->final_data |= CLOSURE_BARRIER_FAILURE_BIT;
}
- if (closure->final_data < 2) {
- grpc_exec_ctx_enqueue(exec_ctx, closure, closure->final_data == 0, NULL);
+ if (closure->final_data < CLOSURE_BARRIER_FIRST_REF_BIT) {
+ if (closure->final_data & CLOSURE_BARRIER_STATS_BIT) {
+ grpc_transport_move_stats(&stream_global->stats,
+ stream_global->collecting_stats);
+ stream_global->collecting_stats = NULL;
+ }
+ grpc_exec_ctx_enqueue(
+ exec_ctx, closure,
+ (closure->final_data & CLOSURE_BARRIER_FAILURE_BIT) == 0, NULL);
}
*pclosure = NULL;
}
@@ -807,7 +819,13 @@ static void perform_stream_op_locked(
}
/* use final_data as a barrier until enqueue time; the inital counter is
dropped at the end of this function */
- on_complete->final_data = 2;
+ on_complete->final_data = CLOSURE_BARRIER_FIRST_REF_BIT;
+
+ if (op->collect_stats != NULL) {
+ GPR_ASSERT(stream_global->collecting_stats == NULL);
+ stream_global->collecting_stats = op->collect_stats;
+ on_complete->final_data |= CLOSURE_BARRIER_STATS_BIT;
+ }
if (op->cancel_with_status != GRPC_STATUS_OK) {
cancel_from_api(exec_ctx, transport_global, stream_global,
@@ -840,7 +858,8 @@ static void perform_stream_op_locked(
}
} else {
grpc_chttp2_complete_closure_step(
- exec_ctx, &stream_global->send_initial_metadata_finished, 0);
+ exec_ctx, stream_global,
+ &stream_global->send_initial_metadata_finished, 0);
}
}
@@ -850,7 +869,7 @@ static void perform_stream_op_locked(
stream_global->send_message_finished = add_closure_barrier(on_complete);
if (stream_global->write_closed) {
grpc_chttp2_complete_closure_step(
- exec_ctx, &stream_global->send_message_finished, 0);
+ exec_ctx, stream_global, &stream_global->send_message_finished, 0);
} else {
stream_global->send_message = op->send_message;
if (stream_global->id != 0) {
@@ -870,7 +889,8 @@ static void perform_stream_op_locked(
}
if (stream_global->write_closed) {
grpc_chttp2_complete_closure_step(
- exec_ctx, &stream_global->send_trailing_metadata_finished,
+ exec_ctx, stream_global,
+ &stream_global->send_trailing_metadata_finished,
grpc_metadata_batch_is_empty(op->send_trailing_metadata));
} else if (stream_global->id != 0) {
/* TODO(ctiller): check if there's flow control for any outstanding
@@ -909,7 +929,7 @@ static void perform_stream_op_locked(
grpc_chttp2_list_add_check_read_ops(transport_global, stream_global);
}
- grpc_chttp2_complete_closure_step(exec_ctx, &on_complete, 1);
+ grpc_chttp2_complete_closure_step(exec_ctx, stream_global, &on_complete, 1);
GPR_TIMER_END("perform_stream_op_locked", 0);
}
@@ -1080,7 +1100,8 @@ static void check_read_ops(grpc_exec_ctx *exec_ctx,
&stream_global->received_trailing_metadata,
stream_global->recv_trailing_metadata);
grpc_chttp2_complete_closure_step(
- exec_ctx, &stream_global->recv_trailing_metadata_finished, 1);
+ exec_ctx, stream_global,
+ &stream_global->recv_trailing_metadata_finished, 1);
}
}
}
@@ -1131,7 +1152,8 @@ static void cancel_from_api(grpc_exec_ctx *exec_ctx,
&transport_global->qbuf,
grpc_chttp2_rst_stream_create(
stream_global->id,
- (uint32_t)grpc_chttp2_grpc_status_to_http2_error(status)));
+ (uint32_t)grpc_chttp2_grpc_status_to_http2_error(status),
+ &stream_global->stats.outgoing));
}
grpc_chttp2_fake_status(exec_ctx, transport_global, stream_global, status,
NULL);
@@ -1179,10 +1201,12 @@ void grpc_chttp2_fake_status(grpc_exec_ctx *exec_ctx,
static void fail_pending_writes(grpc_exec_ctx *exec_ctx,
grpc_chttp2_stream_global *stream_global) {
grpc_chttp2_complete_closure_step(
- exec_ctx, &stream_global->send_initial_metadata_finished, 0);
+ exec_ctx, stream_global, &stream_global->send_initial_metadata_finished,
+ 0);
grpc_chttp2_complete_closure_step(
- exec_ctx, &stream_global->send_trailing_metadata_finished, 0);
- grpc_chttp2_complete_closure_step(exec_ctx,
+ exec_ctx, stream_global, &stream_global->send_trailing_metadata_finished,
+ 0);
+ grpc_chttp2_complete_closure_step(exec_ctx, stream_global,
&stream_global->send_message_finished, 0);
}
@@ -1319,7 +1343,8 @@ static void close_from_api(grpc_exec_ctx *exec_ctx,
gpr_slice_buffer_add(
&transport_global->qbuf,
- grpc_chttp2_rst_stream_create(stream_global->id, GRPC_CHTTP2_NO_ERROR));
+ grpc_chttp2_rst_stream_create(stream_global->id, GRPC_CHTTP2_NO_ERROR,
+ &stream_global->stats.outgoing));
if (optional_message) {
gpr_slice_ref(*optional_message);
diff --git a/src/core/ext/transport/chttp2/transport/frame_data.c b/src/core/ext/transport/chttp2/transport/frame_data.c
index cdd624647c..baa66e0008 100644
--- a/src/core/ext/transport/chttp2/transport/frame_data.c
+++ b/src/core/ext/transport/chttp2/transport/frame_data.c
@@ -113,11 +113,13 @@ grpc_byte_stream *grpc_chttp2_incoming_frame_queue_pop(
void grpc_chttp2_encode_data(uint32_t id, gpr_slice_buffer *inbuf,
uint32_t write_bytes, int is_eof,
+ grpc_transport_one_way_stats *stats,
gpr_slice_buffer *outbuf) {
gpr_slice hdr;
uint8_t *p;
+ static const size_t header_size = 9;
- hdr = gpr_slice_malloc(9);
+ hdr = gpr_slice_malloc(header_size);
p = GPR_SLICE_START_PTR(hdr);
GPR_ASSERT(write_bytes < (1 << 24));
*p++ = (uint8_t)(write_bytes >> 16);
@@ -132,6 +134,9 @@ void grpc_chttp2_encode_data(uint32_t id, gpr_slice_buffer *inbuf,
gpr_slice_buffer_add(outbuf, hdr);
gpr_slice_buffer_move_first(inbuf, write_bytes, outbuf);
+
+ stats->framing_bytes += header_size;
+ stats->data_bytes += write_bytes;
}
grpc_chttp2_parse_error grpc_chttp2_data_parser_parse(
@@ -156,6 +161,7 @@ grpc_chttp2_parse_error grpc_chttp2_data_parser_parse(
switch (p->state) {
fh_0:
case GRPC_CHTTP2_DATA_FH_0:
+ stream_parsing->stats.incoming.framing_bytes++;
p->frame_type = *cur;
switch (p->frame_type) {
case 0:
@@ -174,6 +180,7 @@ grpc_chttp2_parse_error grpc_chttp2_data_parser_parse(
}
/* fallthrough */
case GRPC_CHTTP2_DATA_FH_1:
+ stream_parsing->stats.incoming.framing_bytes++;
p->frame_size = ((uint32_t)*cur) << 24;
if (++cur == end) {
p->state = GRPC_CHTTP2_DATA_FH_2;
@@ -181,6 +188,7 @@ grpc_chttp2_parse_error grpc_chttp2_data_parser_parse(
}
/* fallthrough */
case GRPC_CHTTP2_DATA_FH_2:
+ stream_parsing->stats.incoming.framing_bytes++;
p->frame_size |= ((uint32_t)*cur) << 16;
if (++cur == end) {
p->state = GRPC_CHTTP2_DATA_FH_3;
@@ -188,6 +196,7 @@ grpc_chttp2_parse_error grpc_chttp2_data_parser_parse(
}
/* fallthrough */
case GRPC_CHTTP2_DATA_FH_3:
+ stream_parsing->stats.incoming.framing_bytes++;
p->frame_size |= ((uint32_t)*cur) << 8;
if (++cur == end) {
p->state = GRPC_CHTTP2_DATA_FH_4;
@@ -195,6 +204,7 @@ grpc_chttp2_parse_error grpc_chttp2_data_parser_parse(
}
/* fallthrough */
case GRPC_CHTTP2_DATA_FH_4:
+ stream_parsing->stats.incoming.framing_bytes++;
p->frame_size |= ((uint32_t)*cur);
p->state = GRPC_CHTTP2_DATA_FRAME;
++cur;
@@ -215,7 +225,9 @@ grpc_chttp2_parse_error grpc_chttp2_data_parser_parse(
}
grpc_chttp2_list_add_parsing_seen_stream(transport_parsing,
stream_parsing);
- if ((uint32_t)(end - cur) == p->frame_size) {
+ uint32_t remaining = (uint32_t)(end - cur);
+ if (remaining == p->frame_size) {
+ stream_parsing->stats.incoming.data_bytes += p->frame_size;
grpc_chttp2_incoming_byte_stream_push(
exec_ctx, p->parsing_frame,
gpr_slice_sub(slice, (size_t)(cur - beg), (size_t)(end - beg)));
@@ -224,7 +236,8 @@ grpc_chttp2_parse_error grpc_chttp2_data_parser_parse(
p->parsing_frame = NULL;
p->state = GRPC_CHTTP2_DATA_FH_0;
return GRPC_CHTTP2_PARSE_OK;
- } else if ((uint32_t)(end - cur) > p->frame_size) {
+ } else if (remaining > p->frame_size) {
+ stream_parsing->stats.incoming.data_bytes += p->frame_size;
grpc_chttp2_incoming_byte_stream_push(
exec_ctx, p->parsing_frame,
gpr_slice_sub(slice, (size_t)(cur - beg),
@@ -235,11 +248,12 @@ grpc_chttp2_parse_error grpc_chttp2_data_parser_parse(
cur += p->frame_size;
goto fh_0; /* loop */
} else {
+ GPR_ASSERT(remaining <= p->frame_size);
grpc_chttp2_incoming_byte_stream_push(
exec_ctx, p->parsing_frame,
gpr_slice_sub(slice, (size_t)(cur - beg), (size_t)(end - beg)));
- GPR_ASSERT((size_t)(end - cur) <= p->frame_size);
- p->frame_size -= (uint32_t)(end - cur);
+ p->frame_size -= remaining;
+ stream_parsing->stats.incoming.data_bytes += remaining;
return GRPC_CHTTP2_PARSE_OK;
}
}
diff --git a/src/core/ext/transport/chttp2/transport/frame_data.h b/src/core/ext/transport/chttp2/transport/frame_data.h
index f2762ed9de..8a073a9c11 100644
--- a/src/core/ext/transport/chttp2/transport/frame_data.h
+++ b/src/core/ext/transport/chttp2/transport/frame_data.h
@@ -41,6 +41,7 @@
#include "src/core/ext/transport/chttp2/transport/frame.h"
#include "src/core/lib/iomgr/exec_ctx.h"
#include "src/core/lib/transport/byte_stream.h"
+#include "src/core/lib/transport/transport.h"
typedef enum {
GRPC_CHTTP2_DATA_FH_0,
@@ -96,6 +97,7 @@ grpc_chttp2_parse_error grpc_chttp2_data_parser_parse(
void grpc_chttp2_encode_data(uint32_t id, gpr_slice_buffer *inbuf,
uint32_t write_bytes, int is_eof,
+ grpc_transport_one_way_stats *stats,
gpr_slice_buffer *outbuf);
#endif /* GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_FRAME_DATA_H */
diff --git a/src/core/ext/transport/chttp2/transport/frame_rst_stream.c b/src/core/ext/transport/chttp2/transport/frame_rst_stream.c
index acfc3627e8..d9a04bbf3c 100644
--- a/src/core/ext/transport/chttp2/transport/frame_rst_stream.c
+++ b/src/core/ext/transport/chttp2/transport/frame_rst_stream.c
@@ -38,8 +38,11 @@
#include "src/core/ext/transport/chttp2/transport/frame.h"
-gpr_slice grpc_chttp2_rst_stream_create(uint32_t id, uint32_t code) {
- gpr_slice slice = gpr_slice_malloc(13);
+gpr_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;
+ gpr_slice slice = gpr_slice_malloc(frame_size);
+ stats->framing_bytes += frame_size;
uint8_t *p = GPR_SLICE_START_PTR(slice);
*p++ = 0;
@@ -84,6 +87,7 @@ grpc_chttp2_parse_error grpc_chttp2_rst_stream_parser_parse(
cur++;
p->byte++;
}
+ stream_parsing->stats.incoming.framing_bytes += (uint64_t)(end - cur);
if (p->byte == 4) {
GPR_ASSERT(is_last);
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 134f1368a0..729274e9a2 100644
--- a/src/core/ext/transport/chttp2/transport/frame_rst_stream.h
+++ b/src/core/ext/transport/chttp2/transport/frame_rst_stream.h
@@ -37,13 +37,15 @@
#include <grpc/support/slice.h>
#include "src/core/ext/transport/chttp2/transport/frame.h"
#include "src/core/lib/iomgr/exec_ctx.h"
+#include "src/core/lib/transport/transport.h"
typedef struct {
uint8_t byte;
uint8_t reason_bytes[4];
} grpc_chttp2_rst_stream_parser;
-gpr_slice grpc_chttp2_rst_stream_create(uint32_t stream_id, uint32_t code);
+gpr_slice grpc_chttp2_rst_stream_create(uint32_t stream_id, uint32_t code,
+ grpc_transport_one_way_stats *stats);
grpc_chttp2_parse_error grpc_chttp2_rst_stream_parser_begin_frame(
grpc_chttp2_rst_stream_parser *parser, uint32_t length, uint8_t flags);
diff --git a/src/core/ext/transport/chttp2/transport/frame_window_update.c b/src/core/ext/transport/chttp2/transport/frame_window_update.c
index 1a561cebaf..0baf6d2fdd 100644
--- a/src/core/ext/transport/chttp2/transport/frame_window_update.c
+++ b/src/core/ext/transport/chttp2/transport/frame_window_update.c
@@ -36,9 +36,11 @@
#include <grpc/support/log.h>
-gpr_slice grpc_chttp2_window_update_create(uint32_t id,
- uint32_t window_update) {
- gpr_slice slice = gpr_slice_malloc(13);
+gpr_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;
+ gpr_slice slice = gpr_slice_malloc(frame_size);
+ stats->header_bytes += frame_size;
uint8_t *p = GPR_SLICE_START_PTR(slice);
GPR_ASSERT(window_update);
@@ -87,6 +89,10 @@ grpc_chttp2_parse_error grpc_chttp2_window_update_parser_parse(
p->byte++;
}
+ if (stream_parsing != NULL) {
+ stream_parsing->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)) {
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 f9f670b6f1..a1093a6041 100644
--- a/src/core/ext/transport/chttp2/transport/frame_window_update.h
+++ b/src/core/ext/transport/chttp2/transport/frame_window_update.h
@@ -37,6 +37,7 @@
#include <grpc/support/slice.h>
#include "src/core/ext/transport/chttp2/transport/frame.h"
#include "src/core/lib/iomgr/exec_ctx.h"
+#include "src/core/lib/transport/transport.h"
typedef struct {
uint8_t byte;
@@ -44,7 +45,8 @@ typedef struct {
uint32_t amount;
} grpc_chttp2_window_update_parser;
-gpr_slice grpc_chttp2_window_update_create(uint32_t id, uint32_t window_delta);
+gpr_slice grpc_chttp2_window_update_create(uint32_t id, uint32_t window_delta,
+ grpc_transport_one_way_stats *stats);
grpc_chttp2_parse_error grpc_chttp2_window_update_parser_begin_frame(
grpc_chttp2_window_update_parser *parser, uint32_t length, uint8_t flags);
diff --git a/src/core/ext/transport/chttp2/transport/hpack_encoder.c b/src/core/ext/transport/chttp2/transport/hpack_encoder.c
index 819addd9e3..880305afdd 100644
--- a/src/core/ext/transport/chttp2/transport/hpack_encoder.c
+++ b/src/core/ext/transport/chttp2/transport/hpack_encoder.c
@@ -74,6 +74,7 @@ typedef struct {
/* output stream id */
uint32_t stream_id;
gpr_slice_buffer *output;
+ grpc_transport_one_way_stats *stats;
} framer_state;
/* fills p (which is expected to be 9 bytes long) with a data frame header */
@@ -102,6 +103,7 @@ static void finish_frame(framer_state *st, int is_header_boundary,
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;
}
@@ -147,8 +149,10 @@ static void add_header_data(framer_state *st, gpr_slice slice) {
remaining = GRPC_CHTTP2_MAX_PAYLOAD_LENGTH +
st->output_length_at_start_of_frame - st->output->length;
if (len <= remaining) {
+ st->stats->header_bytes += len;
gpr_slice_buffer_add(st->output, slice);
} else {
+ st->stats->header_bytes += remaining;
gpr_slice_buffer_add(st->output, gpr_slice_split_head(&slice, remaining));
finish_frame(st, 0, 0);
begin_frame(st);
@@ -535,6 +539,7 @@ void grpc_chttp2_hpack_compressor_set_max_table_size(
void grpc_chttp2_encode_header(grpc_chttp2_hpack_compressor *c,
uint32_t stream_id,
grpc_metadata_batch *metadata, int is_eof,
+ grpc_transport_one_way_stats *stats,
gpr_slice_buffer *outbuf) {
framer_state st;
grpc_linked_mdelem *l;
@@ -546,6 +551,7 @@ void grpc_chttp2_encode_header(grpc_chttp2_hpack_compressor *c,
st.stream_id = stream_id;
st.output = outbuf;
st.is_first_frame = 1;
+ st.stats = stats;
/* Encode a metadata batch; store the returned values, representing
a metadata element that needs to be unreffed back into the metadata
diff --git a/src/core/ext/transport/chttp2/transport/hpack_encoder.h b/src/core/ext/transport/chttp2/transport/hpack_encoder.h
index d79de35979..91c368fbe2 100644
--- a/src/core/ext/transport/chttp2/transport/hpack_encoder.h
+++ b/src/core/ext/transport/chttp2/transport/hpack_encoder.h
@@ -40,6 +40,7 @@
#include "src/core/ext/transport/chttp2/transport/frame.h"
#include "src/core/lib/transport/metadata.h"
#include "src/core/lib/transport/metadata_batch.h"
+#include "src/core/lib/transport/transport.h"
#define GRPC_CHTTP2_HPACKC_NUM_FILTERS 256
#define GRPC_CHTTP2_HPACKC_NUM_VALUES 256
@@ -90,6 +91,7 @@ void grpc_chttp2_hpack_compressor_set_max_usable_size(
void grpc_chttp2_encode_header(grpc_chttp2_hpack_compressor *c, uint32_t id,
grpc_metadata_batch *metadata, int is_eof,
+ grpc_transport_one_way_stats *stats,
gpr_slice_buffer *outbuf);
#endif /* GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_HPACK_ENCODER_H */
diff --git a/src/core/ext/transport/chttp2/transport/hpack_parser.c b/src/core/ext/transport/chttp2/transport/hpack_parser.c
index ec3387efb8..259452fa42 100644
--- a/src/core/ext/transport/chttp2/transport/hpack_parser.c
+++ b/src/core/ext/transport/chttp2/transport/hpack_parser.c
@@ -1426,6 +1426,9 @@ grpc_chttp2_parse_error grpc_chttp2_header_parser_parse(
grpc_chttp2_stream_parsing *stream_parsing, gpr_slice slice, int is_last) {
grpc_chttp2_hpack_parser *parser = hpack_parser;
GPR_TIMER_BEGIN("grpc_chttp2_hpack_parser_parse", 0);
+ if (stream_parsing != NULL) {
+ stream_parsing->stats.incoming.header_bytes += GPR_SLICE_LENGTH(slice);
+ }
if (!grpc_chttp2_hpack_parser_parse(parser, GPR_SLICE_START_PTR(slice),
GPR_SLICE_END_PTR(slice))) {
GPR_TIMER_END("grpc_chttp2_hpack_parser_parse", 0);
diff --git a/src/core/ext/transport/chttp2/transport/internal.h b/src/core/ext/transport/chttp2/transport/internal.h
index 2fae653623..2c3c7a3820 100644
--- a/src/core/ext/transport/chttp2/transport/internal.h
+++ b/src/core/ext/transport/chttp2/transport/internal.h
@@ -394,6 +394,9 @@ typedef struct {
grpc_metadata_batch *recv_trailing_metadata;
grpc_closure *recv_trailing_metadata_finished;
+ grpc_transport_stream_stats *collecting_stats;
+ grpc_transport_stream_stats stats;
+
/** when the application requests writes be closed, the write_closed is
'queued'; when the close is flow controlled into the send path, we are
'sending' it; when the write has been performed it is 'sent' */
@@ -435,6 +438,8 @@ typedef struct {
gpr_slice fetching_slice;
size_t stream_fetched;
grpc_closure finished_fetch;
+ /** stats gathered during the write */
+ grpc_transport_one_way_stats stats;
} grpc_chttp2_stream_writing;
struct grpc_chttp2_stream_parsing {
@@ -460,6 +465,8 @@ struct grpc_chttp2_stream_parsing {
int64_t outgoing_window;
/** number of bytes received - reset at end of parse thread execution */
int64_t received_bytes;
+ /** stats gathered during the parse */
+ grpc_transport_stream_stats stats;
/** incoming metadata */
grpc_chttp2_incoming_metadata_buffer metadata_buffer[2];
@@ -635,6 +642,7 @@ void grpc_chttp2_parsing_become_skip_parser(
grpc_exec_ctx *exec_ctx, grpc_chttp2_transport_parsing *transport_parsing);
void grpc_chttp2_complete_closure_step(grpc_exec_ctx *exec_ctx,
+ grpc_chttp2_stream_global *stream_global,
grpc_closure **pclosure, int success);
#define GRPC_CHTTP2_CLIENT_CONNECT_STRING "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n"
diff --git a/src/core/ext/transport/chttp2/transport/parsing.c b/src/core/ext/transport/chttp2/transport/parsing.c
index a9f043cee3..1e815e096b 100644
--- a/src/core/ext/transport/chttp2/transport/parsing.c
+++ b/src/core/ext/transport/chttp2/transport/parsing.c
@@ -171,6 +171,9 @@ void grpc_chttp2_publish_reads(
grpc_chttp2_list_add_check_read_ops(transport_global, stream_global);
}
+ /* flush stats to global stream state */
+ grpc_transport_move_stats(&stream_parsing->stats, &stream_global->stats);
+
/* update outgoing flow control window */
was_zero = stream_global->outgoing_window <= 0;
GRPC_CHTTP2_FLOW_MOVE_STREAM("parsed", transport_global, stream_global,
@@ -544,8 +547,13 @@ static int init_data_frame_parser(
grpc_chttp2_parsing_lookup_stream(transport_parsing,
transport_parsing->incoming_stream_id);
grpc_chttp2_parse_error err = GRPC_CHTTP2_PARSE_OK;
- if (!stream_parsing || stream_parsing->received_close)
+ if (stream_parsing == NULL) {
+ return init_skip_frame_parser(exec_ctx, transport_parsing, 0);
+ }
+ stream_parsing->stats.incoming.framing_bytes += 9;
+ if (stream_parsing->received_close) {
return init_skip_frame_parser(exec_ctx, transport_parsing, 0);
+ }
if (err == GRPC_CHTTP2_PARSE_OK) {
err = update_incoming_window(exec_ctx, transport_parsing, stream_parsing);
}
@@ -566,7 +574,8 @@ static int init_data_frame_parser(
gpr_slice_buffer_add(
&transport_parsing->qbuf,
grpc_chttp2_rst_stream_create(transport_parsing->incoming_stream_id,
- GRPC_CHTTP2_PROTOCOL_ERROR));
+ GRPC_CHTTP2_PROTOCOL_ERROR,
+ &stream_parsing->stats.outgoing));
return init_skip_frame_parser(exec_ctx, transport_parsing, 0);
case GRPC_CHTTP2_CONNECTION_ERROR:
return 0;
@@ -717,6 +726,7 @@ static int init_header_frame_parser(
transport_parsing->incoming_stream = stream_parsing;
}
GPR_ASSERT(stream_parsing != NULL && (via_accept == 0 || via_accept == 1));
+ stream_parsing->stats.incoming.framing_bytes += 9;
if (stream_parsing->received_close) {
gpr_log(GPR_ERROR, "skipping already closed grpc_chttp2_stream header");
transport_parsing->incoming_stream = NULL;
@@ -752,9 +762,14 @@ static int init_window_update_frame_parser(
&transport_parsing->simple.window_update,
transport_parsing->incoming_frame_size,
transport_parsing->incoming_frame_flags);
- if (transport_parsing->incoming_stream_id) {
- transport_parsing->incoming_stream = grpc_chttp2_parsing_lookup_stream(
- transport_parsing, transport_parsing->incoming_stream_id);
+ if (transport_parsing->incoming_stream_id != 0) {
+ grpc_chttp2_stream_parsing *stream_parsing =
+ transport_parsing->incoming_stream = grpc_chttp2_parsing_lookup_stream(
+ transport_parsing, transport_parsing->incoming_stream_id);
+ if (stream_parsing == NULL) {
+ return init_skip_frame_parser(exec_ctx, transport_parsing, 0);
+ }
+ stream_parsing->stats.incoming.framing_bytes += 9;
}
transport_parsing->parser = grpc_chttp2_window_update_parser_parse;
transport_parsing->parser_data = &transport_parsing->simple.window_update;
@@ -778,11 +793,13 @@ static int init_rst_stream_parser(
&transport_parsing->simple.rst_stream,
transport_parsing->incoming_frame_size,
transport_parsing->incoming_frame_flags);
- transport_parsing->incoming_stream = grpc_chttp2_parsing_lookup_stream(
- transport_parsing, transport_parsing->incoming_stream_id);
+ grpc_chttp2_stream_parsing *stream_parsing =
+ transport_parsing->incoming_stream = grpc_chttp2_parsing_lookup_stream(
+ transport_parsing, transport_parsing->incoming_stream_id);
if (!transport_parsing->incoming_stream) {
return init_skip_frame_parser(exec_ctx, transport_parsing, 0);
}
+ stream_parsing->stats.incoming.framing_bytes += 9;
transport_parsing->parser = grpc_chttp2_rst_stream_parser_parse;
transport_parsing->parser_data = &transport_parsing->simple.rst_stream;
return ok;
@@ -856,7 +873,8 @@ static int parse_frame_slice(grpc_exec_ctx *exec_ctx,
gpr_slice_buffer_add(
&transport_parsing->qbuf,
grpc_chttp2_rst_stream_create(transport_parsing->incoming_stream_id,
- GRPC_CHTTP2_PROTOCOL_ERROR));
+ GRPC_CHTTP2_PROTOCOL_ERROR,
+ &stream_parsing->stats.outgoing));
}
return 1;
case GRPC_CHTTP2_CONNECTION_ERROR:
diff --git a/src/core/ext/transport/chttp2/transport/writing.c b/src/core/ext/transport/chttp2/transport/writing.c
index 8d5886f225..6bb188f1da 100644
--- a/src/core/ext/transport/chttp2/transport/writing.c
+++ b/src/core/ext/transport/chttp2/transport/writing.c
@@ -161,8 +161,10 @@ int grpc_chttp2_unlocking_check_writes(
transport_global->announce_incoming_window, UINT32_MAX);
GRPC_CHTTP2_FLOW_DEBIT_TRANSPORT("write", transport_global,
announce_incoming_window, announced);
- gpr_slice_buffer_add(&transport_writing->outbuf,
- grpc_chttp2_window_update_create(0, announced));
+ grpc_transport_one_way_stats throwaway_stats;
+ gpr_slice_buffer_add(
+ &transport_writing->outbuf,
+ grpc_chttp2_window_update_create(0, announced, &throwaway_stats));
}
GPR_TIMER_END("grpc_chttp2_unlocking_check_writes", 0);
@@ -205,7 +207,8 @@ static void finalize_outbuf(grpc_exec_ctx *exec_ctx,
if (stream_writing->send_initial_metadata != NULL) {
grpc_chttp2_encode_header(
&transport_writing->hpack_compressor, stream_writing->id,
- stream_writing->send_initial_metadata, 0, &transport_writing->outbuf);
+ stream_writing->send_initial_metadata, 0, &stream_writing->stats,
+ &transport_writing->outbuf);
stream_writing->send_initial_metadata = NULL;
stream_writing->sent_initial_metadata = 1;
}
@@ -216,7 +219,8 @@ static void finalize_outbuf(grpc_exec_ctx *exec_ctx,
gpr_slice_buffer_add(
&transport_writing->outbuf,
grpc_chttp2_window_update_create(stream_writing->id,
- stream_writing->announce_window));
+ stream_writing->announce_window,
+ &stream_writing->stats));
GRPC_CHTTP2_FLOW_DEBIT_STREAM("write", transport_writing, stream_writing,
announce_window, announce);
stream_writing->announce_window = 0;
@@ -255,7 +259,8 @@ static void finalize_outbuf(grpc_exec_ctx *exec_ctx,
stream_writing->send_trailing_metadata);
grpc_chttp2_encode_data(
stream_writing->id, &stream_writing->flow_controlled_buffer,
- send_bytes, is_last_frame, &transport_writing->outbuf);
+ send_bytes, is_last_frame, &stream_writing->stats,
+ &transport_writing->outbuf);
GRPC_CHTTP2_FLOW_DEBIT_STREAM("write", transport_writing,
stream_writing, outgoing_window,
send_bytes);
@@ -281,19 +286,20 @@ static void finalize_outbuf(grpc_exec_ctx *exec_ctx,
stream_writing->send_trailing_metadata != NULL) {
if (grpc_metadata_batch_is_empty(
stream_writing->send_trailing_metadata)) {
- grpc_chttp2_encode_data(stream_writing->id,
- &stream_writing->flow_controlled_buffer, 0, 1,
- &transport_writing->outbuf);
+ grpc_chttp2_encode_data(
+ stream_writing->id, &stream_writing->flow_controlled_buffer, 0, 1,
+ &stream_writing->stats, &transport_writing->outbuf);
} else {
- grpc_chttp2_encode_header(&transport_writing->hpack_compressor,
- stream_writing->id,
- stream_writing->send_trailing_metadata, 1,
- &transport_writing->outbuf);
+ grpc_chttp2_encode_header(
+ &transport_writing->hpack_compressor, stream_writing->id,
+ stream_writing->send_trailing_metadata, 1, &stream_writing->stats,
+ &transport_writing->outbuf);
}
if (!transport_writing->is_client && !stream_writing->read_closed) {
gpr_slice_buffer_add(&transport_writing->outbuf,
grpc_chttp2_rst_stream_create(
- stream_writing->id, GRPC_CHTTP2_NO_ERROR));
+ stream_writing->id, GRPC_CHTTP2_NO_ERROR,
+ &stream_writing->stats));
}
stream_writing->send_trailing_metadata = NULL;
stream_writing->sent_trailing_metadata = 1;
@@ -328,17 +334,21 @@ void grpc_chttp2_cleanup_writing(
transport_global, transport_writing, &stream_global, &stream_writing)) {
if (stream_writing->sent_initial_metadata) {
grpc_chttp2_complete_closure_step(
- exec_ctx, &stream_global->send_initial_metadata_finished, 1);
+ exec_ctx, stream_global,
+ &stream_global->send_initial_metadata_finished, 1);
}
+ grpc_transport_move_one_way_stats(&stream_writing->stats,
+ &stream_global->stats.outgoing);
if (stream_writing->sent_message) {
GPR_ASSERT(stream_writing->send_message == NULL);
grpc_chttp2_complete_closure_step(
- exec_ctx, &stream_global->send_message_finished, 1);
+ exec_ctx, stream_global, &stream_global->send_message_finished, 1);
stream_writing->sent_message = 0;
}
if (stream_writing->sent_trailing_metadata) {
grpc_chttp2_complete_closure_step(
- exec_ctx, &stream_global->send_trailing_metadata_finished, 1);
+ exec_ctx, stream_global,
+ &stream_global->send_trailing_metadata_finished, 1);
}
if (stream_writing->sent_trailing_metadata) {
grpc_chttp2_mark_stream_closed(exec_ctx, transport_global, stream_global,