Index: third_party/grpc/src/core/client_config/subchannel_index.c |
diff --git a/third_party/grpc/src/core/client_config/subchannel_index.c b/third_party/grpc/src/core/client_config/subchannel_index.c |
new file mode 100644 |
index 0000000000000000000000000000000000000000..3f948998f9cae1f4f4d81ae46c2ce51ffdedcc7b |
--- /dev/null |
+++ b/third_party/grpc/src/core/client_config/subchannel_index.c |
@@ -0,0 +1,261 @@ |
+// |
+// |
+// 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/client_config/subchannel_index.h" |
+ |
+#include <stdbool.h> |
+#include <string.h> |
+ |
+#include <grpc/support/alloc.h> |
+#include <grpc/support/avl.h> |
+#include <grpc/support/tls.h> |
+ |
+#include "src/core/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; |
+ |
+struct grpc_subchannel_key { |
+ grpc_connector *connector; |
+ grpc_subchannel_args args; |
+}; |
+ |
+GPR_TLS_DECL(subchannel_index_exec_ctx); |
+ |
+static void enter_ctx(grpc_exec_ctx *exec_ctx) { |
+ GPR_ASSERT(gpr_tls_get(&subchannel_index_exec_ctx) == 0); |
+ gpr_tls_set(&subchannel_index_exec_ctx, (intptr_t)exec_ctx); |
+} |
+ |
+static void leave_ctx(grpc_exec_ctx *exec_ctx) { |
+ GPR_ASSERT(gpr_tls_get(&subchannel_index_exec_ctx) == (intptr_t)exec_ctx); |
+ gpr_tls_set(&subchannel_index_exec_ctx, 0); |
+} |
+ |
+static grpc_exec_ctx *current_ctx() { |
+ grpc_exec_ctx *c = (grpc_exec_ctx *)gpr_tls_get(&subchannel_index_exec_ctx); |
+ GPR_ASSERT(c != NULL); |
+ return c; |
+} |
+ |
+static grpc_subchannel_key *create_key( |
+ grpc_connector *connector, grpc_subchannel_args *args, |
+ grpc_channel_args *(*copy_channel_args)(const grpc_channel_args *args)) { |
+ grpc_subchannel_key *k = gpr_malloc(sizeof(*k)); |
+ k->connector = grpc_connector_ref(connector); |
+ k->args.filter_count = args->filter_count; |
+ k->args.filters = 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); |
+ k->args.addr_len = args->addr_len; |
+ k->args.addr = gpr_malloc(args->addr_len); |
+ memcpy(k->args.addr, args->addr, k->args.addr_len); |
+ k->args.args = copy_channel_args(args->args); |
+ return k; |
+} |
+ |
+grpc_subchannel_key *grpc_subchannel_key_create(grpc_connector *connector, |
+ grpc_subchannel_args *args) { |
+ return create_key(connector, args, grpc_channel_args_normalize); |
+} |
+ |
+static grpc_subchannel_key *subchannel_key_copy(grpc_subchannel_key *k) { |
+ return create_key(k->connector, &k->args, grpc_channel_args_copy); |
+} |
+ |
+static int subchannel_key_compare(grpc_subchannel_key *a, |
+ grpc_subchannel_key *b) { |
+ int c = GPR_ICMP(a->connector, b->connector); |
+ if (c != 0) return c; |
+ c = GPR_ICMP(a->args.addr_len, b->args.addr_len); |
+ if (c != 0) return c; |
+ c = GPR_ICMP(a->args.filter_count, b->args.filter_count); |
+ if (c != 0) return c; |
+ c = memcmp(a->args.addr, b->args.addr, a->args.addr_len); |
+ if (c != 0) return c; |
+ c = memcmp(a->args.filters, b->args.filters, |
+ a->args.filter_count * sizeof(*a->args.filters)); |
+ 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) { |
+ grpc_connector_unref(exec_ctx, k->connector); |
+ gpr_free(k->args.addr); |
+ gpr_free((grpc_channel_args *)k->args.filters); |
+ grpc_channel_args_destroy((grpc_channel_args *)k->args.args); |
+ gpr_free(k); |
+} |
+ |
+static void sck_avl_destroy(void *p) { |
+ grpc_subchannel_key_destroy(current_ctx(), p); |
+} |
+ |
+static void *sck_avl_copy(void *p) { return subchannel_key_copy(p); } |
+ |
+static long sck_avl_compare(void *a, void *b) { |
+ return subchannel_key_compare(a, b); |
+} |
+ |
+static void scv_avl_destroy(void *p) { |
+ GRPC_SUBCHANNEL_WEAK_UNREF(current_ctx(), p, "subchannel_index"); |
+} |
+ |
+static void *scv_avl_copy(void *p) { |
+ GRPC_SUBCHANNEL_WEAK_REF(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_tls_init(&subchannel_index_exec_ctx); |
+} |
+ |
+void grpc_subchannel_index_shutdown(void) { |
+ gpr_mu_destroy(&g_mu); |
+ gpr_avl_unref(g_subchannel_index); |
+ gpr_tls_destroy(&subchannel_index_exec_ctx); |
+} |
+ |
+grpc_subchannel *grpc_subchannel_index_find(grpc_exec_ctx *exec_ctx, |
+ grpc_subchannel_key *key) { |
+ enter_ctx(exec_ctx); |
+ |
+ // 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); |
+ gpr_mu_unlock(&g_mu); |
+ |
+ grpc_subchannel *c = |
+ GRPC_SUBCHANNEL_REF_FROM_WEAK_REF(gpr_avl_get(index, key), "index_find"); |
+ gpr_avl_unref(index); |
+ |
+ leave_ctx(exec_ctx); |
+ return c; |
+} |
+ |
+grpc_subchannel *grpc_subchannel_index_register(grpc_exec_ctx *exec_ctx, |
+ grpc_subchannel_key *key, |
+ grpc_subchannel *constructed) { |
+ enter_ctx(exec_ctx); |
+ |
+ grpc_subchannel *c = NULL; |
+ |
+ while (c == NULL) { |
+ // 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); |
+ gpr_mu_unlock(&g_mu); |
+ |
+ // - Check to see if a subchannel already exists |
+ c = gpr_avl_get(index, key); |
+ if (c != NULL) { |
+ // yes -> we're done |
+ GRPC_SUBCHANNEL_WEAK_UNREF(exec_ctx, constructed, "index_register"); |
+ } else { |
+ // no -> update the avl and compare/swap |
+ gpr_avl updated = |
+ gpr_avl_add(gpr_avl_ref(index), subchannel_key_copy(key), |
+ GRPC_SUBCHANNEL_WEAK_REF(constructed, "index_register")); |
+ |
+ // 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); |
+ } |
+ gpr_avl_unref(index); |
+ } |
+ |
+ leave_ctx(exec_ctx); |
+ |
+ return c; |
+} |
+ |
+void grpc_subchannel_index_unregister(grpc_exec_ctx *exec_ctx, |
+ grpc_subchannel_key *key, |
+ grpc_subchannel *constructed) { |
+ enter_ctx(exec_ctx); |
+ |
+ 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); |
+ gpr_mu_unlock(&g_mu); |
+ |
+ // Check to see if this key still refers to the previously |
+ // registered subchannel |
+ grpc_subchannel *c = gpr_avl_get(index, key); |
+ if (c != constructed) { |
+ gpr_avl_unref(index); |
+ 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), key); |
+ |
+ 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); |
+ gpr_avl_unref(index); |
+ } |
+ |
+ leave_ctx(exec_ctx); |
+} |