From bb7d088ed2ea79bdad2917b7be86bad622d1f790 Mon Sep 17 00:00:00 2001
From: Vizerai <jsking@google.com>
Date: Mon, 23 Oct 2017 12:06:29 -0700
Subject: Cleaning up census code.

---
 grpc.def | 28 ----------------------------
 1 file changed, 28 deletions(-)

(limited to 'grpc.def')

diff --git a/grpc.def b/grpc.def
index 558be60c3c..4fc521f406 100644
--- a/grpc.def
+++ b/grpc.def
@@ -1,32 +1,4 @@
 EXPORTS
-    census_initialize
-    census_shutdown
-    census_supported
-    census_enabled
-    census_context_create
-    census_context_destroy
-    census_context_get_status
-    census_context_initialize_iterator
-    census_context_next_tag
-    census_context_get_tag
-    census_context_encode
-    census_context_decode
-    census_trace_mask
-    census_set_trace_mask
-    census_start_rpc_op_timestamp
-    census_start_client_rpc_op
-    census_set_rpc_client_peer
-    census_start_server_rpc_op
-    census_start_op
-    census_end_op
-    census_trace_print
-    census_trace_scan_start
-    census_get_trace_record
-    census_trace_scan_end
-    census_define_resource
-    census_delete_resource
-    census_resource_id
-    census_record_values
     grpc_compression_algorithm_parse
     grpc_compression_algorithm_name
     grpc_stream_compression_algorithm_name
-- 
cgit v1.2.3


From 42bd87e376913939850bfa78a3c7f96ce83af11e Mon Sep 17 00:00:00 2001
From: Ken Payson <kpayson@google.com>
Date: Fri, 20 Oct 2017 10:32:30 -0700
Subject: Adds gRPC Experimental CQ DoThenAsyncNext lambda API

---
 grpc.def                                       |   2 +
 include/grpc++/impl/codegen/completion_queue.h |  39 +++++++++
 include/grpc/grpc.h                            |  17 ++++
 src/core/lib/surface/completion_queue.cc       | 116 ++++++++++++++++++-------
 src/core/lib/surface/completion_queue.h        |   3 +
 src/core/lib/surface/init.cc                   |   1 +
 src/cpp/common/completion_queue_cc.cc          |  25 ++++++
 src/ruby/ext/grpc/rb_grpc_imports.generated.c  |   4 +
 src/ruby/ext/grpc/rb_grpc_imports.generated.h  |   6 ++
 test/core/surface/completion_queue_test.c      |  76 ++++++++++++++++
 test/cpp/end2end/async_end2end_test.cc         | 111 ++++++++++++++++++++++-
 test/cpp/qps/client.h                          |  44 ++++------
 test/cpp/qps/client_async.cc                   |  48 ++++++----
 test/cpp/qps/client_sync.cc                    |  39 ++++++---
 test/cpp/qps/server_async.cc                   |  31 ++++---
 15 files changed, 466 insertions(+), 96 deletions(-)

(limited to 'grpc.def')

diff --git a/grpc.def b/grpc.def
index 558be60c3c..e4281f3ab6 100644
--- a/grpc.def
+++ b/grpc.def
@@ -54,6 +54,8 @@ EXPORTS
     grpc_completion_queue_pluck
     grpc_completion_queue_shutdown
     grpc_completion_queue_destroy
+    grpc_completion_queue_thread_local_cache_init
+    grpc_completion_queue_thread_local_cache_flush
     grpc_alarm_create
     grpc_alarm_set
     grpc_alarm_cancel
diff --git a/include/grpc++/impl/codegen/completion_queue.h b/include/grpc++/impl/codegen/completion_queue.h
index ca757e2a9c..e2c0c29dca 100644
--- a/include/grpc++/impl/codegen/completion_queue.h
+++ b/include/grpc++/impl/codegen/completion_queue.h
@@ -109,6 +109,30 @@ class CompletionQueue : private GrpcLibraryCodegen {
     TIMEOUT     ///< deadline was reached.
   };
 
+  /// EXPERIMENTAL
+  /// First executes \a F, then reads from the queue, blocking up to
+  /// \a deadline (or the queue's shutdown).
+  /// Both \a tag and \a ok are updated upon success (if an event is available
+  /// within the \a deadline).  A \a tag points to an arbitrary location usually
+  /// employed to uniquely identify an event.
+  ///
+  /// \param F[in] Function to execute before calling AsyncNext on this queue.
+  /// \param tag[out] Upon sucess, updated to point to the event's tag.
+  /// \param ok[out] Upon sucess, true if read a regular event, false otherwise.
+  /// \param deadline[in] How long to block in wait for an event.
+  ///
+  /// \return The type of event read.
+  template <typename T, typename F>
+  NextStatus DoThenAsyncNext(F&& f, void** tag, bool* ok, const T& deadline) {
+    CompletionQueueTLSCache cache = CompletionQueueTLSCache(this);
+    f();
+    if (cache.Flush(tag, ok)) {
+      return GOT_EVENT;
+    } else {
+      return AsyncNext(tag, ok, deadline);
+    }
+  }
+
   /// Read from the queue, blocking up to \a deadline (or the queue's shutdown).
   /// Both \a tag and \a ok are updated upon success (if an event is available
   /// within the \a deadline).  A \a tag points to an arbitrary location usually
@@ -213,6 +237,21 @@ class CompletionQueue : private GrpcLibraryCodegen {
                                   const InputMessage& request,
                                   OutputMessage* result);
 
+  /// EXPERIMENTAL
+  /// Creates a Thread Local cache to store the first event
+  /// On this completion queue queued from this thread.  Once
+  /// initialized, it must be flushed on the same thread.
+  class CompletionQueueTLSCache {
+   public:
+    CompletionQueueTLSCache(CompletionQueue* cq);
+    ~CompletionQueueTLSCache();
+    bool Flush(void** tag, bool* ok);
+
+   private:
+    CompletionQueue* cq_;
+    bool flushed_;
+  };
+
   NextStatus AsyncNextInternal(void** tag, bool* ok, gpr_timespec deadline);
 
   /// Wraps \a grpc_completion_queue_pluck.
diff --git a/include/grpc/grpc.h b/include/grpc/grpc.h
index 1de289fba4..6df3b8086e 100644
--- a/include/grpc/grpc.h
+++ b/include/grpc/grpc.h
@@ -143,6 +143,23 @@ GRPCAPI void grpc_completion_queue_shutdown(grpc_completion_queue *cq);
     drained and no threads are executing grpc_completion_queue_next */
 GRPCAPI void grpc_completion_queue_destroy(grpc_completion_queue *cq);
 
+/*********** EXPERIMENTAL API ************/
+/** Initializes a thread local cache for \a cq.
+  * grpc_flush_cq_tls_cache() MUST be called on the same thread,
+  * with the same cq.
+  */
+GRPCAPI void grpc_completion_queue_thread_local_cache_init(
+    grpc_completion_queue *cq);
+
+/*********** EXPERIMENTAL API ************/
+/** Flushes the thread local cache for \a cq.
+  * Returns 1 if there was contents in the cache.  If there was an event
+  * in \a cq tls cache, its tag is placed in tag, and ok is set to the
+  * event success.
+  */
+GRPCAPI int grpc_completion_queue_thread_local_cache_flush(
+    grpc_completion_queue *cq, void **tag, int *ok);
+
 /** Create a completion queue alarm instance */
 GRPCAPI grpc_alarm *grpc_alarm_create(void *reserved);
 
diff --git a/src/core/lib/surface/completion_queue.cc b/src/core/lib/surface/completion_queue.cc
index 21664f03c8..5009f786e6 100644
--- a/src/core/lib/surface/completion_queue.cc
+++ b/src/core/lib/surface/completion_queue.cc
@@ -28,6 +28,7 @@
 #include <grpc/support/log.h>
 #include <grpc/support/string_util.h>
 #include <grpc/support/time.h>
+#include <grpc/support/tls.h>
 
 #include "src/core/lib/debug/stats.h"
 #include "src/core/lib/iomgr/pollset.h"
@@ -48,6 +49,14 @@ grpc_tracer_flag grpc_trace_cq_refcount =
     GRPC_TRACER_INITIALIZER(false, "cq_refcount");
 #endif
 
+// Specifies a cq thread local cache.
+// The first event that occurs on a thread
+// with a cq cache will go into that cache, and
+// will only be returned on the thread that initialized the cache.
+// NOTE: Only one event will ever be cached.
+GPR_TLS_DECL(g_cached_event);
+GPR_TLS_DECL(g_cached_cq);
+
 typedef struct {
   grpc_pollset_worker **worker;
   void *tag;
@@ -345,6 +354,46 @@ grpc_tracer_flag grpc_cq_event_timeout_trace =
 static void on_pollset_shutdown_done(grpc_exec_ctx *exec_ctx, void *cq,
                                      grpc_error *error);
 
+void grpc_cq_global_init() {
+  gpr_tls_init(&g_cached_event);
+  gpr_tls_init(&g_cached_cq);
+}
+
+void grpc_completion_queue_thread_local_cache_init(grpc_completion_queue *cq) {
+  if ((grpc_completion_queue *)gpr_tls_get(&g_cached_cq) == nullptr) {
+    gpr_tls_set(&g_cached_event, (intptr_t)0);
+    gpr_tls_set(&g_cached_cq, (intptr_t)cq);
+  }
+}
+
+int grpc_completion_queue_thread_local_cache_flush(grpc_completion_queue *cq,
+                                                   void **tag, int *ok) {
+  grpc_cq_completion *storage =
+      (grpc_cq_completion *)gpr_tls_get(&g_cached_event);
+  int ret = 0;
+  if (storage != NULL &&
+      (grpc_completion_queue *)gpr_tls_get(&g_cached_cq) == cq) {
+    *tag = storage->tag;
+    grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
+    storage->done(&exec_ctx, storage->done_arg, storage);
+    *ok = (storage->next & (uintptr_t)(1)) == 1;
+    ret = 1;
+    cq_next_data *cqd = (cq_next_data *)DATA_FROM_CQ(cq);
+    if (gpr_atm_full_fetch_add(&cqd->pending_events, -1) == 1) {
+      GRPC_CQ_INTERNAL_REF(cq, "shutting_down");
+      gpr_mu_lock(cq->mu);
+      cq_finish_shutdown_next(&exec_ctx, cq);
+      gpr_mu_unlock(cq->mu);
+      GRPC_CQ_INTERNAL_UNREF(&exec_ctx, cq, "shutting_down");
+    }
+    grpc_exec_ctx_finish(&exec_ctx);
+  }
+  gpr_tls_set(&g_cached_event, (intptr_t)0);
+  gpr_tls_set(&g_cached_cq, (intptr_t)0);
+
+  return ret;
+}
+
 static void cq_event_queue_init(grpc_cq_event_queue *q) {
   gpr_mpscq_init(&q->queue);
   q->queue_lock = GPR_SPINLOCK_INITIALIZER;
@@ -617,7 +666,6 @@ static void cq_end_op_for_next(grpc_exec_ctx *exec_ctx,
       gpr_log(GPR_ERROR, "Operation failed: tag=%p, error=%s", tag, errmsg);
     }
   }
-
   cq_next_data *cqd = (cq_next_data *)DATA_FROM_CQ(cq);
   int is_success = (error == GRPC_ERROR_NONE);
 
@@ -628,44 +676,50 @@ static void cq_end_op_for_next(grpc_exec_ctx *exec_ctx,
 
   cq_check_tag(cq, tag, true); /* Used in debug builds only */
 
-  /* Add the completion to the queue */
-  bool is_first = cq_event_queue_push(&cqd->queue, storage);
-  gpr_atm_no_barrier_fetch_add(&cqd->things_queued_ever, 1);
-
-  /* Since we do not hold the cq lock here, it is important to do an 'acquire'
-     load here (instead of a 'no_barrier' load) to match with the release store
-     (done via gpr_atm_full_fetch_add(pending_events, -1)) in cq_shutdown_next
-     */
-  bool will_definitely_shutdown = gpr_atm_acq_load(&cqd->pending_events) == 1;
-
-  if (!will_definitely_shutdown) {
-    /* Only kick if this is the first item queued */
-    if (is_first) {
-      gpr_mu_lock(cq->mu);
-      grpc_error *kick_error =
-          cq->poller_vtable->kick(exec_ctx, POLLSET_FROM_CQ(cq), NULL);
-      gpr_mu_unlock(cq->mu);
+  if ((grpc_completion_queue *)gpr_tls_get(&g_cached_cq) == cq &&
+      (grpc_cq_completion *)gpr_tls_get(&g_cached_event) == nullptr) {
+    gpr_tls_set(&g_cached_event, (intptr_t)storage);
+  } else {
+    /* Add the completion to the queue */
+    bool is_first = cq_event_queue_push(&cqd->queue, storage);
+    gpr_atm_no_barrier_fetch_add(&cqd->things_queued_ever, 1);
+
+    /* Since we do not hold the cq lock here, it is important to do an 'acquire'
+       load here (instead of a 'no_barrier' load) to match with the release
+       store
+       (done via gpr_atm_full_fetch_add(pending_events, -1)) in cq_shutdown_next
+       */
+    bool will_definitely_shutdown = gpr_atm_acq_load(&cqd->pending_events) == 1;
+
+    if (!will_definitely_shutdown) {
+      /* Only kick if this is the first item queued */
+      if (is_first) {
+        gpr_mu_lock(cq->mu);
+        grpc_error *kick_error =
+            cq->poller_vtable->kick(exec_ctx, POLLSET_FROM_CQ(cq), NULL);
+        gpr_mu_unlock(cq->mu);
 
-      if (kick_error != GRPC_ERROR_NONE) {
-        const char *msg = grpc_error_string(kick_error);
-        gpr_log(GPR_ERROR, "Kick failed: %s", msg);
-        GRPC_ERROR_UNREF(kick_error);
+        if (kick_error != GRPC_ERROR_NONE) {
+          const char *msg = grpc_error_string(kick_error);
+          gpr_log(GPR_ERROR, "Kick failed: %s", msg);
+          GRPC_ERROR_UNREF(kick_error);
+        }
       }
-    }
-    if (gpr_atm_full_fetch_add(&cqd->pending_events, -1) == 1) {
+      if (gpr_atm_full_fetch_add(&cqd->pending_events, -1) == 1) {
+        GRPC_CQ_INTERNAL_REF(cq, "shutting_down");
+        gpr_mu_lock(cq->mu);
+        cq_finish_shutdown_next(exec_ctx, cq);
+        gpr_mu_unlock(cq->mu);
+        GRPC_CQ_INTERNAL_UNREF(exec_ctx, cq, "shutting_down");
+      }
+    } else {
       GRPC_CQ_INTERNAL_REF(cq, "shutting_down");
+      gpr_atm_rel_store(&cqd->pending_events, 0);
       gpr_mu_lock(cq->mu);
       cq_finish_shutdown_next(exec_ctx, cq);
       gpr_mu_unlock(cq->mu);
       GRPC_CQ_INTERNAL_UNREF(exec_ctx, cq, "shutting_down");
     }
-  } else {
-    GRPC_CQ_INTERNAL_REF(cq, "shutting_down");
-    gpr_atm_rel_store(&cqd->pending_events, 0);
-    gpr_mu_lock(cq->mu);
-    cq_finish_shutdown_next(exec_ctx, cq);
-    gpr_mu_unlock(cq->mu);
-    GRPC_CQ_INTERNAL_UNREF(exec_ctx, cq, "shutting_down");
   }
 
   GPR_TIMER_END("cq_end_op_for_next", 0);
diff --git a/src/core/lib/surface/completion_queue.h b/src/core/lib/surface/completion_queue.h
index 69d144bd95..c02bc5da07 100644
--- a/src/core/lib/surface/completion_queue.h
+++ b/src/core/lib/surface/completion_queue.h
@@ -70,6 +70,9 @@ void grpc_cq_internal_unref(grpc_exec_ctx *exec_ctx, grpc_completion_queue *cc);
 #define GRPC_CQ_INTERNAL_UNREF(ec, cc, reason) grpc_cq_internal_unref(ec, cc)
 #endif
 
+/* Initializes global variables used by completion queues */
+void grpc_cq_global_init();
+
 /* Flag that an operation is beginning: the completion channel will not finish
    shutdown until a corrensponding grpc_cq_end_* call is made.
    \a tag is currently used only in debug builds. Return true on success, and
diff --git a/src/core/lib/surface/init.cc b/src/core/lib/surface/init.cc
index b089da2c54..058e88f804 100644
--- a/src/core/lib/surface/init.cc
+++ b/src/core/lib/surface/init.cc
@@ -64,6 +64,7 @@ static void do_basic_init(void) {
   gpr_log_verbosity_init();
   gpr_mu_init(&g_init_mu);
   grpc_register_built_in_plugins();
+  grpc_cq_global_init();
   g_initializations = 0;
 }
 
diff --git a/src/cpp/common/completion_queue_cc.cc b/src/cpp/common/completion_queue_cc.cc
index f34b0f3d58..4a2e2be688 100644
--- a/src/cpp/common/completion_queue_cc.cc
+++ b/src/cpp/common/completion_queue_cc.cc
@@ -71,4 +71,29 @@ CompletionQueue::NextStatus CompletionQueue::AsyncNextInternal(
   }
 }
 
+CompletionQueue::CompletionQueueTLSCache::CompletionQueueTLSCache(
+    CompletionQueue* cq)
+    : cq_(cq), flushed_(false) {
+  grpc_completion_queue_thread_local_cache_init(cq_->cq_);
+}
+
+CompletionQueue::CompletionQueueTLSCache::~CompletionQueueTLSCache() {
+  GPR_ASSERT(flushed_);
+}
+
+bool CompletionQueue::CompletionQueueTLSCache::Flush(void** tag, bool* ok) {
+  int res = 0;
+  void* res_tag;
+  flushed_ = true;
+  if (grpc_completion_queue_thread_local_cache_flush(cq_->cq_, &res_tag,
+                                                     &res)) {
+    auto cq_tag = static_cast<CompletionQueueTag*>(res_tag);
+    *ok = res == 1;
+    if (cq_tag->FinalizeResult(tag, ok)) {
+      return true;
+    }
+  }
+  return false;
+}
+
 }  // namespace grpc
diff --git a/src/ruby/ext/grpc/rb_grpc_imports.generated.c b/src/ruby/ext/grpc/rb_grpc_imports.generated.c
index 70831494fa..cd1bd98abc 100644
--- a/src/ruby/ext/grpc/rb_grpc_imports.generated.c
+++ b/src/ruby/ext/grpc/rb_grpc_imports.generated.c
@@ -77,6 +77,8 @@ grpc_completion_queue_next_type grpc_completion_queue_next_import;
 grpc_completion_queue_pluck_type grpc_completion_queue_pluck_import;
 grpc_completion_queue_shutdown_type grpc_completion_queue_shutdown_import;
 grpc_completion_queue_destroy_type grpc_completion_queue_destroy_import;
+grpc_completion_queue_thread_local_cache_init_type grpc_completion_queue_thread_local_cache_init_import;
+grpc_completion_queue_thread_local_cache_flush_type grpc_completion_queue_thread_local_cache_flush_import;
 grpc_alarm_create_type grpc_alarm_create_import;
 grpc_alarm_set_type grpc_alarm_set_import;
 grpc_alarm_cancel_type grpc_alarm_cancel_import;
@@ -385,6 +387,8 @@ void grpc_rb_load_imports(HMODULE library) {
   grpc_completion_queue_pluck_import = (grpc_completion_queue_pluck_type) GetProcAddress(library, "grpc_completion_queue_pluck");
   grpc_completion_queue_shutdown_import = (grpc_completion_queue_shutdown_type) GetProcAddress(library, "grpc_completion_queue_shutdown");
   grpc_completion_queue_destroy_import = (grpc_completion_queue_destroy_type) GetProcAddress(library, "grpc_completion_queue_destroy");
+  grpc_completion_queue_thread_local_cache_init_import = (grpc_completion_queue_thread_local_cache_init_type) GetProcAddress(library, "grpc_completion_queue_thread_local_cache_init");
+  grpc_completion_queue_thread_local_cache_flush_import = (grpc_completion_queue_thread_local_cache_flush_type) GetProcAddress(library, "grpc_completion_queue_thread_local_cache_flush");
   grpc_alarm_create_import = (grpc_alarm_create_type) GetProcAddress(library, "grpc_alarm_create");
   grpc_alarm_set_import = (grpc_alarm_set_type) GetProcAddress(library, "grpc_alarm_set");
   grpc_alarm_cancel_import = (grpc_alarm_cancel_type) GetProcAddress(library, "grpc_alarm_cancel");
diff --git a/src/ruby/ext/grpc/rb_grpc_imports.generated.h b/src/ruby/ext/grpc/rb_grpc_imports.generated.h
index 868772cfc8..c7e78b70dc 100644
--- a/src/ruby/ext/grpc/rb_grpc_imports.generated.h
+++ b/src/ruby/ext/grpc/rb_grpc_imports.generated.h
@@ -212,6 +212,12 @@ extern grpc_completion_queue_shutdown_type grpc_completion_queue_shutdown_import
 typedef void(*grpc_completion_queue_destroy_type)(grpc_completion_queue *cq);
 extern grpc_completion_queue_destroy_type grpc_completion_queue_destroy_import;
 #define grpc_completion_queue_destroy grpc_completion_queue_destroy_import
+typedef void(*grpc_completion_queue_thread_local_cache_init_type)(grpc_completion_queue *cq);
+extern grpc_completion_queue_thread_local_cache_init_type grpc_completion_queue_thread_local_cache_init_import;
+#define grpc_completion_queue_thread_local_cache_init grpc_completion_queue_thread_local_cache_init_import
+typedef int(*grpc_completion_queue_thread_local_cache_flush_type)(grpc_completion_queue *cq, void **tag, int *ok);
+extern grpc_completion_queue_thread_local_cache_flush_type grpc_completion_queue_thread_local_cache_flush_import;
+#define grpc_completion_queue_thread_local_cache_flush grpc_completion_queue_thread_local_cache_flush_import
 typedef grpc_alarm *(*grpc_alarm_create_type)(void *reserved);
 extern grpc_alarm_create_type grpc_alarm_create_import;
 #define grpc_alarm_create grpc_alarm_create_import
diff --git a/test/core/surface/completion_queue_test.c b/test/core/surface/completion_queue_test.c
index e6372a379c..e4e4c9f1b2 100644
--- a/test/core/surface/completion_queue_test.c
+++ b/test/core/surface/completion_queue_test.c
@@ -158,6 +158,80 @@ static void test_cq_end_op(void) {
   }
 }
 
+static void test_cq_tls_cache_full(void) {
+  grpc_event ev;
+  grpc_completion_queue *cc;
+  grpc_cq_completion completion;
+  grpc_cq_polling_type polling_types[] = {
+      GRPC_CQ_DEFAULT_POLLING, GRPC_CQ_NON_LISTENING, GRPC_CQ_NON_POLLING};
+  grpc_completion_queue_attributes attr;
+  grpc_exec_ctx init_exec_ctx = GRPC_EXEC_CTX_INIT;
+  grpc_exec_ctx exec_ctx;
+  void *tag = create_test_tag();
+  void *res_tag;
+  int ok;
+
+  LOG_TEST("test_cq_tls_cache_full");
+
+  attr.version = 1;
+  attr.cq_completion_type = GRPC_CQ_NEXT;
+  for (size_t i = 0; i < GPR_ARRAY_SIZE(polling_types); i++) {
+    exec_ctx = init_exec_ctx;  // Reset exec_ctx
+    attr.cq_polling_type = polling_types[i];
+    cc = grpc_completion_queue_create(
+        grpc_completion_queue_factory_lookup(&attr), &attr, NULL);
+
+    grpc_completion_queue_thread_local_cache_init(cc);
+    GPR_ASSERT(grpc_cq_begin_op(cc, tag));
+    grpc_cq_end_op(&exec_ctx, cc, tag, GRPC_ERROR_NONE,
+                   do_nothing_end_completion, NULL, &completion);
+
+    ev = grpc_completion_queue_next(cc, gpr_inf_past(GPR_CLOCK_REALTIME), NULL);
+    GPR_ASSERT(ev.type == GRPC_QUEUE_TIMEOUT);
+
+    GPR_ASSERT(
+        grpc_completion_queue_thread_local_cache_flush(cc, &res_tag, &ok) == 1);
+    GPR_ASSERT(res_tag == tag);
+    GPR_ASSERT(ok);
+
+    ev = grpc_completion_queue_next(cc, gpr_inf_past(GPR_CLOCK_REALTIME), NULL);
+    GPR_ASSERT(ev.type == GRPC_QUEUE_TIMEOUT);
+
+    shutdown_and_destroy(cc);
+    grpc_exec_ctx_finish(&exec_ctx);
+  }
+}
+
+static void test_cq_tls_cache_empty(void) {
+  grpc_completion_queue *cc;
+  grpc_cq_polling_type polling_types[] = {
+      GRPC_CQ_DEFAULT_POLLING, GRPC_CQ_NON_LISTENING, GRPC_CQ_NON_POLLING};
+  grpc_completion_queue_attributes attr;
+  grpc_exec_ctx init_exec_ctx = GRPC_EXEC_CTX_INIT;
+  grpc_exec_ctx exec_ctx;
+  void *res_tag;
+  int ok;
+
+  LOG_TEST("test_cq_tls_cache_empty");
+
+  attr.version = 1;
+  attr.cq_completion_type = GRPC_CQ_NEXT;
+  for (size_t i = 0; i < GPR_ARRAY_SIZE(polling_types); i++) {
+    exec_ctx = init_exec_ctx;  // Reset exec_ctx
+    attr.cq_polling_type = polling_types[i];
+    cc = grpc_completion_queue_create(
+        grpc_completion_queue_factory_lookup(&attr), &attr, NULL);
+
+    GPR_ASSERT(
+        grpc_completion_queue_thread_local_cache_flush(cc, &res_tag, &ok) == 0);
+    grpc_completion_queue_thread_local_cache_init(cc);
+    GPR_ASSERT(
+        grpc_completion_queue_thread_local_cache_flush(cc, &res_tag, &ok) == 0);
+    shutdown_and_destroy(cc);
+    grpc_exec_ctx_finish(&exec_ctx);
+  }
+}
+
 static void test_shutdown_then_next_polling(void) {
   grpc_cq_polling_type polling_types[] = {
       GRPC_CQ_DEFAULT_POLLING, GRPC_CQ_NON_LISTENING, GRPC_CQ_NON_POLLING};
@@ -300,6 +374,8 @@ int main(int argc, char **argv) {
   test_cq_end_op();
   test_pluck();
   test_pluck_after_shutdown();
+  test_cq_tls_cache_full();
+  test_cq_tls_cache_empty();
   grpc_shutdown();
   return 0;
 }
diff --git a/test/cpp/end2end/async_end2end_test.cc b/test/cpp/end2end/async_end2end_test.cc
index 2a33e8ae11..b7634d0438 100644
--- a/test/cpp/end2end/async_end2end_test.cc
+++ b/test/cpp/end2end/async_end2end_test.cc
@@ -99,7 +99,7 @@ class PollingOverrider {
 
 class Verifier {
  public:
-  explicit Verifier(bool spin) : spin_(spin) {}
+  explicit Verifier(bool spin) : spin_(spin), lambda_run_(false) {}
   // Expect sets the expected ok value for a specific tag
   Verifier& Expect(int i, bool expect_ok) {
     return ExpectUnless(i, expect_ok, false);
@@ -142,6 +142,18 @@ class Verifier {
     return detag(got_tag);
   }
 
+  template <typename T>
+  CompletionQueue::NextStatus DoOnceThenAsyncNext(
+      CompletionQueue* cq, void** got_tag, bool* ok, T deadline,
+      std::function<void(void)> lambda) {
+    if (lambda_run_) {
+      return cq->AsyncNext(got_tag, ok, deadline);
+    } else {
+      lambda_run_ = true;
+      return cq->DoThenAsyncNext(lambda, got_tag, ok, deadline);
+    }
+  }
+
   // Verify keeps calling Next until all currently set
   // expected tags are complete
   void Verify(CompletionQueue* cq) { Verify(cq, false); }
@@ -154,6 +166,7 @@ class Verifier {
       Next(cq, ignore_ok);
     }
   }
+
   // This version of Verify stops after a certain deadline
   void Verify(CompletionQueue* cq,
               std::chrono::system_clock::time_point deadline) {
@@ -193,6 +206,47 @@ class Verifier {
     }
   }
 
+  // This version of Verify stops after a certain deadline, and uses the
+  // DoThenAsyncNext API
+  // to call the lambda
+  void Verify(CompletionQueue* cq,
+              std::chrono::system_clock::time_point deadline,
+              std::function<void(void)> lambda) {
+    if (expectations_.empty()) {
+      bool ok;
+      void* got_tag;
+      if (spin_) {
+        while (std::chrono::system_clock::now() < deadline) {
+          EXPECT_EQ(DoOnceThenAsyncNext(cq, &got_tag, &ok, deadline, lambda),
+                    CompletionQueue::TIMEOUT);
+        }
+      } else {
+        EXPECT_EQ(DoOnceThenAsyncNext(cq, &got_tag, &ok, deadline, lambda),
+                  CompletionQueue::TIMEOUT);
+      }
+    } else {
+      while (!expectations_.empty()) {
+        bool ok;
+        void* got_tag;
+        if (spin_) {
+          for (;;) {
+            GPR_ASSERT(std::chrono::system_clock::now() < deadline);
+            auto r = DoOnceThenAsyncNext(
+                cq, &got_tag, &ok, gpr_time_0(GPR_CLOCK_REALTIME), lambda);
+            if (r == CompletionQueue::TIMEOUT) continue;
+            if (r == CompletionQueue::GOT_EVENT) break;
+            gpr_log(GPR_ERROR, "unexpected result from AsyncNext");
+            abort();
+          }
+        } else {
+          EXPECT_EQ(DoOnceThenAsyncNext(cq, &got_tag, &ok, deadline, lambda),
+                    CompletionQueue::GOT_EVENT);
+        }
+        GotTag(got_tag, ok, false);
+      }
+    }
+  }
+
  private:
   void GotTag(void* got_tag, bool ok, bool ignore_ok) {
     auto it = expectations_.find(got_tag);
@@ -226,6 +280,7 @@ class Verifier {
   std::map<void*, bool> expectations_;
   std::map<void*, MaybeExpect> maybe_expectations_;
   bool spin_;
+  bool lambda_run_;
 };
 
 bool plugin_has_sync_methods(std::unique_ptr<ServerBuilderPlugin>& plugin) {
@@ -490,6 +545,60 @@ TEST_P(AsyncEnd2endTest, AsyncNextRpc) {
   EXPECT_TRUE(recv_status.ok());
 }
 
+// Test a simple RPC using the async version of Next
+TEST_P(AsyncEnd2endTest, DoThenAsyncNextRpc) {
+  ResetStub();
+
+  EchoRequest send_request;
+  EchoRequest recv_request;
+  EchoResponse send_response;
+  EchoResponse recv_response;
+  Status recv_status;
+
+  ClientContext cli_ctx;
+  ServerContext srv_ctx;
+  grpc::ServerAsyncResponseWriter<EchoResponse> response_writer(&srv_ctx);
+
+  send_request.set_message(GetParam().message_content);
+  std::unique_ptr<ClientAsyncResponseReader<EchoResponse>> response_reader(
+      stub_->AsyncEcho(&cli_ctx, send_request, cq_.get()));
+
+  std::chrono::system_clock::time_point time_now(
+      std::chrono::system_clock::now());
+  std::chrono::system_clock::time_point time_limit(
+      std::chrono::system_clock::now() + std::chrono::seconds(10));
+  Verifier(GetParam().disable_blocking).Verify(cq_.get(), time_now);
+  Verifier(GetParam().disable_blocking).Verify(cq_.get(), time_now);
+
+  auto resp_writer_ptr = &response_writer;
+  auto lambda_2 = [&, this, resp_writer_ptr]() {
+    gpr_log(GPR_ERROR, "CALLED");
+    service_->RequestEcho(&srv_ctx, &recv_request, resp_writer_ptr, cq_.get(),
+                          cq_.get(), tag(2));
+  };
+
+  Verifier(GetParam().disable_blocking)
+      .Expect(2, true)
+      .Verify(cq_.get(), time_limit, lambda_2);
+  EXPECT_EQ(send_request.message(), recv_request.message());
+
+  auto recv_resp_ptr = &recv_response;
+  auto status_ptr = &recv_status;
+  send_response.set_message(recv_request.message());
+  auto lambda_3 = [&, this, resp_writer_ptr, send_response]() {
+    resp_writer_ptr->Finish(send_response, Status::OK, tag(3));
+  };
+  response_reader->Finish(recv_resp_ptr, status_ptr, tag(4));
+  Verifier(GetParam().disable_blocking)
+      .Expect(3, true)
+      .Expect(4, true)
+      .Verify(cq_.get(), std::chrono::system_clock::time_point::max(),
+              lambda_3);
+
+  EXPECT_EQ(send_response.message(), recv_response.message());
+  EXPECT_TRUE(recv_status.ok());
+}
+
 // Two pings and a final pong.
 TEST_P(AsyncEnd2endTest, SimpleClientStreaming) {
   ResetStub();
diff --git a/test/cpp/qps/client.h b/test/cpp/qps/client.h
index abf755b393..9888c762f2 100644
--- a/test/cpp/qps/client.h
+++ b/test/cpp/qps/client.h
@@ -226,8 +226,6 @@ class Client {
   }
 
   virtual void DestroyMultithreading() = 0;
-  virtual void InitThreadFunc(size_t thread_idx) = 0;
-  virtual bool ThreadFunc(HistogramEntry* histogram, size_t thread_idx) = 0;
 
   void SetupLoadTest(const ClientConfig& config, size_t num_threads) {
     // Set up the load distribution based on the number of threads
@@ -275,7 +273,6 @@ class Client {
                         : std::bind(&Client::NextIssueTime, this, thread_idx);
   }
 
- private:
   class Thread {
    public:
     Thread(Client* client, size_t idx)
@@ -295,6 +292,16 @@ class Client {
       MergeStatusHistogram(statuses_, s);
     }
 
+    void UpdateHistogram(HistogramEntry* entry) {
+      std::lock_guard<std::mutex> g(mu_);
+      if (entry->value_used()) {
+        histogram_.Add(entry->value());
+      }
+      if (entry->status_used()) {
+        statuses_[entry->status()]++;
+      }
+    }
+
    private:
     Thread(const Thread&);
     Thread& operator=(const Thread&);
@@ -310,29 +317,8 @@ class Client {
         wait_loop++;
       }
 
-      client_->InitThreadFunc(idx_);
-
-      for (;;) {
-        // run the loop body
-        HistogramEntry entry;
-        const bool thread_still_ok = client_->ThreadFunc(&entry, idx_);
-        // lock, update histogram if needed and see if we're done
-        std::lock_guard<std::mutex> g(mu_);
-        if (entry.value_used()) {
-          histogram_.Add(entry.value());
-        }
-        if (entry.status_used()) {
-          statuses_[entry.status()]++;
-        }
-        if (!thread_still_ok) {
-          gpr_log(GPR_ERROR, "Finishing client thread due to RPC error");
-        }
-        if (!thread_still_ok ||
-            static_cast<bool>(gpr_atm_acq_load(&client_->thread_pool_done_))) {
-          client_->CompleteThread();
-          return;
-        }
-      }
+      client_->ThreadFunc(idx_, this);
+      client_->CompleteThread();
     }
 
     std::mutex mu_;
@@ -343,6 +329,12 @@ class Client {
     std::thread impl_;
   };
 
+  bool ThreadCompleted() {
+    return static_cast<bool>(gpr_atm_acq_load(&thread_pool_done_));
+  }
+
+  virtual void ThreadFunc(size_t thread_idx, Client::Thread* t) = 0;
+
   std::vector<std::unique_ptr<Thread>> threads_;
   std::unique_ptr<UsageTimer> timer_;
 
diff --git a/test/cpp/qps/client_async.cc b/test/cpp/qps/client_async.cc
index 9ed4e0b355..b5c7208664 100644
--- a/test/cpp/qps/client_async.cc
+++ b/test/cpp/qps/client_async.cc
@@ -236,33 +236,47 @@ class AsyncClient : public ClientImpl<StubType, RequestType> {
     this->EndThreads();  // this needed for resolution
   }
 
-  void InitThreadFunc(size_t thread_idx) override final {}
-  bool ThreadFunc(HistogramEntry* entry, size_t thread_idx) override final {
+  void ThreadFunc(size_t thread_idx, Client::Thread* t) override final {
     void* got_tag;
     bool ok;
 
-    if (cli_cqs_[cq_[thread_idx]]->Next(&got_tag, &ok)) {
+    HistogramEntry entry;
+    HistogramEntry* entry_ptr = &entry;
+    if (!cli_cqs_[cq_[thread_idx]]->Next(&got_tag, &ok)) {
+      return;
+    }
+    ClientRpcContext* ctx;
+    std::mutex* shutdown_mu = &shutdown_state_[thread_idx]->mutex;
+    do {
+      t->UpdateHistogram(entry_ptr);
       // Got a regular event, so process it
-      ClientRpcContext* ctx = ClientRpcContext::detag(got_tag);
+      ctx = ClientRpcContext::detag(got_tag);
       // Proceed while holding a lock to make sure that
       // this thread isn't supposed to shut down
-      std::lock_guard<std::mutex> l(shutdown_state_[thread_idx]->mutex);
+      shutdown_mu->lock();
       if (shutdown_state_[thread_idx]->shutdown) {
         ctx->TryCancel();
         delete ctx;
-        return true;
-      }
-      if (!ctx->RunNextState(ok, entry)) {
-        // The RPC and callback are done, so clone the ctx
-        // and kickstart the new one
-        ctx->StartNewClone(cli_cqs_[cq_[thread_idx]].get());
-        delete ctx;
+        while (cli_cqs_[cq_[thread_idx]]->Next(&got_tag, &ok)) {
+          ctx = ClientRpcContext::detag(got_tag);
+          ctx->TryCancel();
+          delete ctx;
+        }
+        shutdown_mu->unlock();
+        return;
       }
-      return true;
-    } else {
-      // queue is shutting down, so we must be  done
-      return true;
-    }
+    } while (cli_cqs_[cq_[thread_idx]]->DoThenAsyncNext(
+        [&, ctx, ok, entry_ptr, shutdown_mu]() {
+          bool next_ok = ok;
+          if (!ctx->RunNextState(next_ok, entry_ptr)) {
+            // The RPC and callback are done, so clone the ctx
+            // and kickstart the new one
+            ctx->StartNewClone(cli_cqs_[cq_[thread_idx]].get());
+            delete ctx;
+          }
+          shutdown_mu->unlock();
+        },
+        &got_tag, &ok, gpr_inf_future(GPR_CLOCK_REALTIME)));
   }
 
   std::vector<std::unique_ptr<CompletionQueue>> cli_cqs_;
diff --git a/test/cpp/qps/client_sync.cc b/test/cpp/qps/client_sync.cc
index 94554a46b2..9f20b148eb 100644
--- a/test/cpp/qps/client_sync.cc
+++ b/test/cpp/qps/client_sync.cc
@@ -62,6 +62,25 @@ class SynchronousClient
 
   virtual ~SynchronousClient(){};
 
+  virtual void InitThreadFuncImpl(size_t thread_idx) = 0;
+  virtual bool ThreadFuncImpl(HistogramEntry* entry, size_t thread_idx) = 0;
+
+  void ThreadFunc(size_t thread_idx, Thread* t) override {
+    InitThreadFuncImpl(thread_idx);
+    for (;;) {
+      // run the loop body
+      HistogramEntry entry;
+      const bool thread_still_ok = ThreadFuncImpl(&entry, thread_idx);
+      t->UpdateHistogram(&entry);
+      if (!thread_still_ok) {
+        gpr_log(GPR_ERROR, "Finishing client thread due to RPC error");
+      }
+      if (!thread_still_ok || ThreadCompleted()) {
+        return;
+      }
+    }
+  }
+
  protected:
   // WaitToIssue returns false if we realize that we need to break out
   bool WaitToIssue(int thread_idx) {
@@ -103,9 +122,9 @@ class SynchronousUnaryClient final : public SynchronousClient {
   }
   ~SynchronousUnaryClient() {}
 
-  void InitThreadFunc(size_t thread_idx) override {}
+  void InitThreadFuncImpl(size_t thread_idx) override {}
 
-  bool ThreadFunc(HistogramEntry* entry, size_t thread_idx) override {
+  bool ThreadFuncImpl(HistogramEntry* entry, size_t thread_idx) override {
     if (!WaitToIssue(thread_idx)) {
       return true;
     }
@@ -192,13 +211,13 @@ class SynchronousStreamingPingPongClient final
     }
   }
 
-  void InitThreadFunc(size_t thread_idx) override {
+  void InitThreadFuncImpl(size_t thread_idx) override {
     auto* stub = channels_[thread_idx % channels_.size()].get_stub();
     stream_[thread_idx] = stub->StreamingCall(&context_[thread_idx]);
     messages_issued_[thread_idx] = 0;
   }
 
-  bool ThreadFunc(HistogramEntry* entry, size_t thread_idx) override {
+  bool ThreadFuncImpl(HistogramEntry* entry, size_t thread_idx) override {
     if (!WaitToIssue(thread_idx)) {
       return true;
     }
@@ -246,14 +265,14 @@ class SynchronousStreamingFromClientClient final
     }
   }
 
-  void InitThreadFunc(size_t thread_idx) override {
+  void InitThreadFuncImpl(size_t thread_idx) override {
     auto* stub = channels_[thread_idx % channels_.size()].get_stub();
     stream_[thread_idx] = stub->StreamingFromClient(&context_[thread_idx],
                                                     &responses_[thread_idx]);
     last_issue_[thread_idx] = UsageTimer::Now();
   }
 
-  bool ThreadFunc(HistogramEntry* entry, size_t thread_idx) override {
+  bool ThreadFuncImpl(HistogramEntry* entry, size_t thread_idx) override {
     // Figure out how to make histogram sensible if this is rate-paced
     if (!WaitToIssue(thread_idx)) {
       return true;
@@ -282,13 +301,13 @@ class SynchronousStreamingFromServerClient final
  public:
   SynchronousStreamingFromServerClient(const ClientConfig& config)
       : SynchronousStreamingClient(config), last_recv_(num_threads_) {}
-  void InitThreadFunc(size_t thread_idx) override {
+  void InitThreadFuncImpl(size_t thread_idx) override {
     auto* stub = channels_[thread_idx % channels_.size()].get_stub();
     stream_[thread_idx] =
         stub->StreamingFromServer(&context_[thread_idx], request_);
     last_recv_[thread_idx] = UsageTimer::Now();
   }
-  bool ThreadFunc(HistogramEntry* entry, size_t thread_idx) override {
+  bool ThreadFuncImpl(HistogramEntry* entry, size_t thread_idx) override {
     GPR_TIMER_SCOPE("SynchronousStreamingFromServerClient::ThreadFunc", 0);
     if (stream_[thread_idx]->Read(&responses_[thread_idx])) {
       double now = UsageTimer::Now();
@@ -328,11 +347,11 @@ class SynchronousStreamingBothWaysClient final
     }
   }
 
-  void InitThreadFunc(size_t thread_idx) override {
+  void InitThreadFuncImpl(size_t thread_idx) override {
     auto* stub = channels_[thread_idx % channels_.size()].get_stub();
     stream_[thread_idx] = stub->StreamingBothWays(&context_[thread_idx]);
   }
-  bool ThreadFunc(HistogramEntry* entry, size_t thread_idx) override {
+  bool ThreadFuncImpl(HistogramEntry* entry, size_t thread_idx) override {
     // TODO (vjpai): Do this
     return true;
   }
diff --git a/test/cpp/qps/server_async.cc b/test/cpp/qps/server_async.cc
index 4a82f98199..c1097cb8ee 100644
--- a/test/cpp/qps/server_async.cc
+++ b/test/cpp/qps/server_async.cc
@@ -194,23 +194,32 @@ class AsyncQpsServerTest final : public grpc::testing::Server {
     // Wait until work is available or we are shutting down
     bool ok;
     void *got_tag;
-    while (srv_cqs_[cq_[thread_idx]]->Next(&got_tag, &ok)) {
-      ServerRpcContext *ctx = detag(got_tag);
+    if (!srv_cqs_[cq_[thread_idx]]->Next(&got_tag, &ok)) {
+      return;
+    }
+    ServerRpcContext *ctx;
+    std::mutex *mu_ptr;
+    do {
+      ctx = detag(got_tag);
       // The tag is a pointer to an RPC context to invoke
       // Proceed while holding a lock to make sure that
       // this thread isn't supposed to shut down
-      std::lock_guard<std::mutex> l(shutdown_state_[thread_idx]->mutex);
+      mu_ptr = &shutdown_state_[thread_idx]->mutex;
+      mu_ptr->lock();
       if (shutdown_state_[thread_idx]->shutdown) {
+        mu_ptr->unlock();
         return;
       }
-      std::lock_guard<ServerRpcContext> l2(*ctx);
-      const bool still_going = ctx->RunNextState(ok);
-      // if this RPC context is done, refresh it
-      if (!still_going) {
-        ctx->Reset();
-      }
-    }
-    return;
+    } while (srv_cqs_[cq_[thread_idx]]->DoThenAsyncNext(
+        [&, ctx, ok, mu_ptr]() {
+          ctx->lock();
+          if (!ctx->RunNextState(ok)) {
+            ctx->Reset();
+          }
+          ctx->unlock();
+          mu_ptr->unlock();
+        },
+        &got_tag, &ok, gpr_inf_future(GPR_CLOCK_REALTIME)));
   }
 
   class ServerRpcContext {
-- 
cgit v1.2.3


From c1d354d7f2621f467e2f37f36498c20e132c3a72 Mon Sep 17 00:00:00 2001
From: Justin Burke <burkej@google.com>
Date: Tue, 19 Sep 2017 15:06:01 -0700
Subject: Support SSL server certificate reloading.

---
 grpc.def                                           |   6 +
 include/grpc/grpc_security.h                       |  74 +++++-
 include/grpc/grpc_security_constants.h             |   7 +
 .../security/credentials/ssl/ssl_credentials.cc    | 156 ++++++++++++-
 .../lib/security/credentials/ssl/ssl_credentials.h |  12 +
 .../lib/security/transport/security_connector.cc   | 251 +++++++++++++++------
 .../lib/security/transport/security_connector.h    |   4 +-
 src/ruby/ext/grpc/rb_grpc_imports.generated.c      |  12 +
 src/ruby/ext/grpc/rb_grpc_imports.generated.h      |  18 ++
 9 files changed, 456 insertions(+), 84 deletions(-)

(limited to 'grpc.def')

diff --git a/grpc.def b/grpc.def
index e4281f3ab6..eea12bdda5 100644
--- a/grpc.def
+++ b/grpc.def
@@ -132,8 +132,14 @@ EXPORTS
     grpc_metadata_credentials_create_from_plugin
     grpc_secure_channel_create
     grpc_server_credentials_release
+    grpc_ssl_server_certificate_config_create
+    grpc_ssl_server_certificate_config_destroy
     grpc_ssl_server_credentials_create
     grpc_ssl_server_credentials_create_ex
+    grpc_ssl_server_credentials_create_options_using_config
+    grpc_ssl_server_credentials_create_options_using_config_fetcher
+    grpc_ssl_server_credentials_options_destroy
+    grpc_ssl_server_credentials_create_with_options
     grpc_server_add_secure_http2_port
     grpc_call_set_credentials
     grpc_server_credentials_set_auth_metadata_processor
diff --git a/include/grpc/grpc_security.h b/include/grpc/grpc_security.h
index 95b1447935..758aaf5b6c 100644
--- a/include/grpc/grpc_security.h
+++ b/include/grpc/grpc_security.h
@@ -316,6 +316,43 @@ typedef struct grpc_server_credentials grpc_server_credentials;
    */
 GRPCAPI void grpc_server_credentials_release(grpc_server_credentials *creds);
 
+/** Server certificate config object holds the server's public certificates and
+   associated private keys, as well as any CA certificates needed for client
+   certificate validation (if applicable). Create using
+   grpc_ssl_server_certificate_config_create(). */
+typedef struct grpc_ssl_server_certificate_config
+    grpc_ssl_server_certificate_config;
+
+/** Creates a grpc_ssl_server_certificate_config object.
+   - pem_roots_cert is the NULL-terminated string containing the PEM encoding of
+     the client root certificates. This parameter may be NULL if the server does
+     not want the client to be authenticated with SSL.
+   - pem_key_cert_pairs is an array private key / certificate chains of the
+     server. This parameter cannot be NULL.
+   - num_key_cert_pairs indicates the number of items in the private_key_files
+     and cert_chain_files parameters. It must be at least 1.
+   - It is the caller's responsibility to free this object via
+     grpc_ssl_server_certificate_config_destroy(). */
+GRPCAPI grpc_ssl_server_certificate_config *
+grpc_ssl_server_certificate_config_create(
+    const char *pem_root_certs,
+    const grpc_ssl_pem_key_cert_pair *pem_key_cert_pairs,
+    size_t num_key_cert_pairs);
+
+/** Destroys a grpc_ssl_server_certificate_config object. */
+GRPCAPI void grpc_ssl_server_certificate_config_destroy(
+    grpc_ssl_server_certificate_config *config);
+
+/** Callback to retrieve updated SSL server certificates, private keys, and
+   trusted CAs (for client authentication).
+    - user_data parameter, if not NULL, contains opaque data to be used by the
+      callback.
+    - Use grpc_ssl_server_certificate_config_create to create the config.
+    - The caller assumes ownership of the config. */
+typedef grpc_ssl_certificate_config_reload_status (
+    *grpc_ssl_server_certificate_config_callback)(
+    void *user_data, grpc_ssl_server_certificate_config **config);
+
 /** Deprecated in favor of grpc_ssl_server_credentials_create_ex.
    Creates an SSL server_credentials object.
    - pem_roots_cert is the NULL-terminated string containing the PEM encoding of
@@ -332,7 +369,8 @@ GRPCAPI grpc_server_credentials *grpc_ssl_server_credentials_create(
     const char *pem_root_certs, grpc_ssl_pem_key_cert_pair *pem_key_cert_pairs,
     size_t num_key_cert_pairs, int force_client_auth, void *reserved);
 
-/** Same as grpc_ssl_server_credentials_create method except uses
+/** Deprecated in favor of grpc_ssl_server_credentials_create_with_options.
+   Same as grpc_ssl_server_credentials_create method except uses
    grpc_ssl_client_certificate_request_type enum to support more ways to
    authenticate client cerificates.*/
 GRPCAPI grpc_server_credentials *grpc_ssl_server_credentials_create_ex(
@@ -341,6 +379,40 @@ GRPCAPI grpc_server_credentials *grpc_ssl_server_credentials_create_ex(
     grpc_ssl_client_certificate_request_type client_certificate_request,
     void *reserved);
 
+typedef struct grpc_ssl_server_credentials_options
+    grpc_ssl_server_credentials_options;
+
+/** Creates an options object using a certificate config. Use this method when
+   the certificates and keys of the SSL server will not change during the
+   server's lifetime.
+   - Takes ownership of the certificate_config parameter. */
+GRPCAPI grpc_ssl_server_credentials_options *
+grpc_ssl_server_credentials_create_options_using_config(
+    grpc_ssl_client_certificate_request_type client_certificate_request,
+    grpc_ssl_server_certificate_config *certificate_config);
+
+/** Creates an options object using a certificate config fetcher. Use this
+   method to reload the certificates and keys of the SSL server without
+   interrupting the operation of the server. Initial certificate config will be
+   fetched during server initialization.
+   - user_data parameter, if not NULL, contains opaque data which will be passed
+     to the fetcher (see definition of
+     grpc_ssl_server_certificate_config_callback). */
+GRPCAPI grpc_ssl_server_credentials_options *
+grpc_ssl_server_credentials_create_options_using_config_fetcher(
+    grpc_ssl_client_certificate_request_type client_certificate_request,
+    grpc_ssl_server_certificate_config_callback cb, void *user_data);
+
+/** Destroys a grpc_ssl_server_credentials_options object. */
+GRPCAPI void grpc_ssl_server_credentials_options_destroy(
+    grpc_ssl_server_credentials_options *options);
+
+/** Creates an SSL server_credentials object using the provided options struct.
+    - Takes ownership of the options parameter. */
+GRPCAPI grpc_server_credentials *
+grpc_ssl_server_credentials_create_with_options(
+    grpc_ssl_server_credentials_options *options);
+
 /** --- Server-side secure ports. --- */
 
 /** Add a HTTP2 over an encrypted link over tcp listener.
diff --git a/include/grpc/grpc_security_constants.h b/include/grpc/grpc_security_constants.h
index fde300dfb1..60e167eb88 100644
--- a/include/grpc/grpc_security_constants.h
+++ b/include/grpc/grpc_security_constants.h
@@ -48,6 +48,13 @@ typedef enum {
   GRPC_SSL_ROOTS_OVERRIDE_FAIL
 } grpc_ssl_roots_override_result;
 
+/** Callback results for dynamically loading a SSL certificate config. */
+typedef enum {
+  GRPC_SSL_CERTIFICATE_CONFIG_RELOAD_UNCHANGED,
+  GRPC_SSL_CERTIFICATE_CONFIG_RELOAD_NEW,
+  GRPC_SSL_CERTIFICATE_CONFIG_RELOAD_FAIL
+} grpc_ssl_certificate_config_reload_status;
+
 typedef enum {
   /** Server does not request client certificate. A client can present a self
      signed or signed certificates if it wishes to do so and they would be
diff --git a/src/core/lib/security/credentials/ssl/ssl_credentials.cc b/src/core/lib/security/credentials/ssl/ssl_credentials.cc
index 8e47aebedb..2085e2b8e7 100644
--- a/src/core/lib/security/credentials/ssl/ssl_credentials.cc
+++ b/src/core/lib/security/credentials/ssl/ssl_credentials.cc
@@ -119,6 +119,12 @@ grpc_channel_credentials *grpc_ssl_credentials_create(
 // SSL Server Credentials.
 //
 
+struct grpc_ssl_server_credentials_options {
+  grpc_ssl_client_certificate_request_type client_certificate_request;
+  grpc_ssl_server_certificate_config *certificate_config;
+  grpc_ssl_server_certificate_config_fetcher *certificate_config_fetcher;
+};
+
 static void ssl_server_destruct(grpc_exec_ctx *exec_ctx,
                                 grpc_server_credentials *creds) {
   grpc_ssl_server_credentials *c = (grpc_ssl_server_credentials *)creds;
@@ -130,9 +136,7 @@ static void ssl_server_destruct(grpc_exec_ctx *exec_ctx,
 static grpc_security_status ssl_server_create_security_connector(
     grpc_exec_ctx *exec_ctx, grpc_server_credentials *creds,
     grpc_server_security_connector **sc) {
-  grpc_ssl_server_credentials *c = (grpc_ssl_server_credentials *)creds;
-  return grpc_ssl_server_security_connector_create(exec_ctx, creds, &c->config,
-                                                   sc);
+  return grpc_ssl_server_security_connector_create(exec_ctx, creds, sc);
 }
 
 static grpc_server_credentials_vtable ssl_server_vtable = {
@@ -170,6 +174,86 @@ static void ssl_build_server_config(
   config->num_key_cert_pairs = num_key_cert_pairs;
 }
 
+grpc_ssl_server_certificate_config *grpc_ssl_server_certificate_config_create(
+    const char *pem_root_certs,
+    const grpc_ssl_pem_key_cert_pair *pem_key_cert_pairs,
+    size_t num_key_cert_pairs) {
+  grpc_ssl_server_certificate_config *config =
+      (grpc_ssl_server_certificate_config *)gpr_zalloc(
+          sizeof(grpc_ssl_server_certificate_config));
+  if (pem_root_certs != NULL) {
+    config->pem_root_certs = gpr_strdup(pem_root_certs);
+  }
+  if (num_key_cert_pairs > 0) {
+    GPR_ASSERT(pem_key_cert_pairs != NULL);
+    config->pem_key_cert_pairs = (grpc_ssl_pem_key_cert_pair *)gpr_zalloc(
+        num_key_cert_pairs * sizeof(grpc_ssl_pem_key_cert_pair));
+  }
+  config->num_key_cert_pairs = num_key_cert_pairs;
+  for (size_t i = 0; i < num_key_cert_pairs; i++) {
+    GPR_ASSERT(pem_key_cert_pairs[i].private_key != NULL);
+    GPR_ASSERT(pem_key_cert_pairs[i].cert_chain != NULL);
+    config->pem_key_cert_pairs[i].cert_chain =
+        gpr_strdup(pem_key_cert_pairs[i].cert_chain);
+    config->pem_key_cert_pairs[i].private_key =
+        gpr_strdup(pem_key_cert_pairs[i].private_key);
+  }
+  return config;
+}
+
+void grpc_ssl_server_certificate_config_destroy(
+    grpc_ssl_server_certificate_config *config) {
+  if (config == NULL) return;
+  for (size_t i = 0; i < config->num_key_cert_pairs; i++) {
+    gpr_free((void *)config->pem_key_cert_pairs[i].private_key);
+    gpr_free((void *)config->pem_key_cert_pairs[i].cert_chain);
+  }
+  gpr_free(config->pem_key_cert_pairs);
+  gpr_free(config->pem_root_certs);
+  gpr_free(config);
+}
+
+grpc_ssl_server_credentials_options *
+grpc_ssl_server_credentials_create_options_using_config(
+    grpc_ssl_client_certificate_request_type client_certificate_request,
+    grpc_ssl_server_certificate_config *config) {
+  grpc_ssl_server_credentials_options *options = NULL;
+  if (config == NULL) {
+    gpr_log(GPR_ERROR, "Certificate config must not be NULL.");
+    goto done;
+  }
+  options = (grpc_ssl_server_credentials_options *)gpr_zalloc(
+      sizeof(grpc_ssl_server_credentials_options));
+  options->client_certificate_request = client_certificate_request;
+  options->certificate_config = config;
+done:
+  return options;
+}
+
+grpc_ssl_server_credentials_options *
+grpc_ssl_server_credentials_create_options_using_config_fetcher(
+    grpc_ssl_client_certificate_request_type client_certificate_request,
+    grpc_ssl_server_certificate_config_callback cb, void *user_data) {
+  if (cb == NULL) {
+    gpr_log(GPR_ERROR, "Invalid certificate config callback parameter.");
+    return NULL;
+  }
+
+  grpc_ssl_server_certificate_config_fetcher *fetcher =
+      (grpc_ssl_server_certificate_config_fetcher *)gpr_zalloc(
+          sizeof(grpc_ssl_server_certificate_config_fetcher));
+  fetcher->cb = cb;
+  fetcher->user_data = user_data;
+
+  grpc_ssl_server_credentials_options *options =
+      (grpc_ssl_server_credentials_options *)gpr_zalloc(
+          sizeof(grpc_ssl_server_credentials_options));
+  options->client_certificate_request = client_certificate_request;
+  options->certificate_config_fetcher = fetcher;
+
+  return options;
+}
+
 grpc_server_credentials *grpc_ssl_server_credentials_create(
     const char *pem_root_certs, grpc_ssl_pem_key_cert_pair *pem_key_cert_pairs,
     size_t num_key_cert_pairs, int force_client_auth, void *reserved) {
@@ -186,8 +270,6 @@ grpc_server_credentials *grpc_ssl_server_credentials_create_ex(
     size_t num_key_cert_pairs,
     grpc_ssl_client_certificate_request_type client_certificate_request,
     void *reserved) {
-  grpc_ssl_server_credentials *c = (grpc_ssl_server_credentials *)gpr_zalloc(
-      sizeof(grpc_ssl_server_credentials));
   GRPC_API_TRACE(
       "grpc_ssl_server_credentials_create_ex("
       "pem_root_certs=%s, pem_key_cert_pairs=%p, num_key_cert_pairs=%lu, "
@@ -195,11 +277,67 @@ grpc_server_credentials *grpc_ssl_server_credentials_create_ex(
       5, (pem_root_certs, pem_key_cert_pairs, (unsigned long)num_key_cert_pairs,
           client_certificate_request, reserved));
   GPR_ASSERT(reserved == NULL);
+
+  grpc_ssl_server_certificate_config *cert_config =
+      grpc_ssl_server_certificate_config_create(
+          pem_root_certs, pem_key_cert_pairs, num_key_cert_pairs);
+  grpc_ssl_server_credentials_options *options =
+      grpc_ssl_server_credentials_create_options_using_config(
+          client_certificate_request, cert_config);
+
+  return grpc_ssl_server_credentials_create_with_options(options);
+}
+
+grpc_server_credentials *grpc_ssl_server_credentials_create_with_options(
+    grpc_ssl_server_credentials_options *options) {
+  grpc_server_credentials *retval = NULL;
+  grpc_ssl_server_credentials *c = NULL;
+
+  if (options == NULL) {
+    gpr_log(GPR_ERROR,
+            "Invalid options trying to create SSL server credentials.");
+    goto done;
+  }
+
+  if (options->certificate_config == NULL &&
+      options->certificate_config_fetcher == NULL) {
+    gpr_log(GPR_ERROR,
+            "SSL server credentials options must specify either "
+            "certificate config or fetcher.");
+    goto done;
+  } else if (options->certificate_config_fetcher != NULL &&
+             options->certificate_config_fetcher->cb == NULL) {
+    gpr_log(GPR_ERROR, "Certificate config fetcher callback must not be NULL.");
+    goto done;
+  }
+
+  c = (grpc_ssl_server_credentials *)gpr_zalloc(
+      sizeof(grpc_ssl_server_credentials));
   c->base.type = GRPC_CHANNEL_CREDENTIALS_TYPE_SSL;
   gpr_ref_init(&c->base.refcount, 1);
   c->base.vtable = &ssl_server_vtable;
-  ssl_build_server_config(pem_root_certs, pem_key_cert_pairs,
-                          num_key_cert_pairs, client_certificate_request,
-                          &c->config);
-  return &c->base;
+
+  if (options->certificate_config_fetcher != NULL) {
+    c->config.client_certificate_request = options->client_certificate_request;
+    c->certificate_config_fetcher = *options->certificate_config_fetcher;
+  } else {
+    ssl_build_server_config(options->certificate_config->pem_root_certs,
+                            options->certificate_config->pem_key_cert_pairs,
+                            options->certificate_config->num_key_cert_pairs,
+                            options->client_certificate_request, &c->config);
+  }
+
+  retval = &c->base;
+
+done:
+  grpc_ssl_server_credentials_options_destroy(options);
+  return retval;
+}
+
+void grpc_ssl_server_credentials_options_destroy(
+    grpc_ssl_server_credentials_options *o) {
+  if (o == NULL) return;
+  gpr_free(o->certificate_config_fetcher);
+  grpc_ssl_server_certificate_config_destroy(o->certificate_config);
+  gpr_free(o);
 }
diff --git a/src/core/lib/security/credentials/ssl/ssl_credentials.h b/src/core/lib/security/credentials/ssl/ssl_credentials.h
index 42e425d9f1..5542484aae 100644
--- a/src/core/lib/security/credentials/ssl/ssl_credentials.h
+++ b/src/core/lib/security/credentials/ssl/ssl_credentials.h
@@ -29,9 +29,21 @@ typedef struct {
   grpc_ssl_config config;
 } grpc_ssl_credentials;
 
+struct grpc_ssl_server_certificate_config {
+  grpc_ssl_pem_key_cert_pair *pem_key_cert_pairs;
+  size_t num_key_cert_pairs;
+  char *pem_root_certs;
+};
+
+typedef struct {
+  grpc_ssl_server_certificate_config_callback cb;
+  void *user_data;
+} grpc_ssl_server_certificate_config_fetcher;
+
 typedef struct {
   grpc_server_credentials base;
   grpc_ssl_server_config config;
+  grpc_ssl_server_certificate_config_fetcher certificate_config_fetcher;
 } grpc_ssl_server_credentials;
 
 tsi_ssl_pem_key_cert_pair *grpc_convert_grpc_to_tsi_cert_pairs(
diff --git a/src/core/lib/security/transport/security_connector.cc b/src/core/lib/security/transport/security_connector.cc
index b050be2129..06160d0caa 100644
--- a/src/core/lib/security/transport/security_connector.cc
+++ b/src/core/lib/security/transport/security_connector.cc
@@ -34,6 +34,7 @@
 #include "src/core/lib/security/context/security_context.h"
 #include "src/core/lib/security/credentials/credentials.h"
 #include "src/core/lib/security/credentials/fake/fake_credentials.h"
+#include "src/core/lib/security/credentials/ssl/ssl_credentials.h"
 #include "src/core/lib/security/transport/lb_targets_info.h"
 #include "src/core/lib/security/transport/secure_endpoint.h"
 #include "src/core/lib/security/transport/security_handshaker.h"
@@ -277,6 +278,30 @@ grpc_security_connector *grpc_security_connector_find_in_args(
   return NULL;
 }
 
+static tsi_client_certificate_request_type
+get_tsi_client_certificate_request_type(
+    grpc_ssl_client_certificate_request_type grpc_request_type) {
+  switch (grpc_request_type) {
+    case GRPC_SSL_DONT_REQUEST_CLIENT_CERTIFICATE:
+      return TSI_DONT_REQUEST_CLIENT_CERTIFICATE;
+
+    case GRPC_SSL_REQUEST_CLIENT_CERTIFICATE_BUT_DONT_VERIFY:
+      return TSI_REQUEST_CLIENT_CERTIFICATE_BUT_DONT_VERIFY;
+
+    case GRPC_SSL_REQUEST_CLIENT_CERTIFICATE_AND_VERIFY:
+      return TSI_REQUEST_CLIENT_CERTIFICATE_AND_VERIFY;
+
+    case GRPC_SSL_REQUEST_AND_REQUIRE_CLIENT_CERTIFICATE_BUT_DONT_VERIFY:
+      return TSI_REQUEST_AND_REQUIRE_CLIENT_CERTIFICATE_BUT_DONT_VERIFY;
+
+    case GRPC_SSL_REQUEST_AND_REQUIRE_CLIENT_CERTIFICATE_AND_VERIFY:
+      return TSI_REQUEST_AND_REQUIRE_CLIENT_CERTIFICATE_AND_VERIFY;
+
+    default:
+      return TSI_DONT_REQUEST_CLIENT_CERTIFICATE;
+  }
+}
+
 /* -- Fake implementation. -- */
 
 typedef struct {
@@ -533,6 +558,15 @@ typedef struct {
   tsi_ssl_server_handshaker_factory *server_handshaker_factory;
 } grpc_ssl_server_security_connector;
 
+static bool server_connector_has_cert_config_fetcher(
+    grpc_ssl_server_security_connector *c) {
+  GPR_ASSERT(c != NULL);
+  grpc_ssl_server_credentials *server_creds =
+      (grpc_ssl_server_credentials *)c->base.server_creds;
+  GPR_ASSERT(server_creds != NULL);
+  return server_creds->certificate_config_fetcher.cb != NULL;
+}
+
 static void ssl_channel_destroy(grpc_exec_ctx *exec_ctx,
                                 grpc_security_connector *sc) {
   grpc_ssl_channel_security_connector *c =
@@ -573,7 +607,6 @@ static void ssl_channel_add_handshakers(grpc_exec_ctx *exec_ctx,
             tsi_result_to_string(result));
     return;
   }
-
   // Create handshakers.
   grpc_handshake_manager_add(
       handshake_mgr,
@@ -581,12 +614,102 @@ static void ssl_channel_add_handshakers(grpc_exec_ctx *exec_ctx,
           exec_ctx, tsi_create_adapter_handshaker(tsi_hs), &sc->base));
 }
 
+static const char **fill_alpn_protocol_strings(size_t *num_alpn_protocols) {
+  GPR_ASSERT(num_alpn_protocols != NULL);
+  *num_alpn_protocols = grpc_chttp2_num_alpn_versions();
+  const char **alpn_protocol_strings =
+      (const char **)gpr_malloc(sizeof(const char *) * (*num_alpn_protocols));
+  for (size_t i = 0; i < *num_alpn_protocols; i++) {
+    alpn_protocol_strings[i] = grpc_chttp2_get_alpn_version_index(i);
+  }
+  return alpn_protocol_strings;
+}
+
+/* Attempts to replace the server_handshaker_factory with a new factory using
+ * the provided grpc_ssl_server_certificate_config. Should new factory creation
+ * fail, the existing factory will not be replaced. Returns true on success (new
+ * factory created). */
+static bool try_replace_server_handshaker_factory(
+    grpc_ssl_server_security_connector *sc,
+    const grpc_ssl_server_certificate_config *config) {
+  if (config == NULL) {
+    gpr_log(GPR_ERROR,
+            "Server certificate config callback returned invalid (NULL) "
+            "config.");
+    return false;
+  }
+  gpr_log(GPR_DEBUG, "Using new server certificate config (%p).", config);
+
+  size_t num_alpn_protocols = 0;
+  const char **alpn_protocol_strings =
+      fill_alpn_protocol_strings(&num_alpn_protocols);
+  tsi_ssl_pem_key_cert_pair *cert_pairs = grpc_convert_grpc_to_tsi_cert_pairs(
+      config->pem_key_cert_pairs, config->num_key_cert_pairs);
+  tsi_ssl_server_handshaker_factory *new_handshaker_factory = NULL;
+  grpc_ssl_server_credentials *server_creds =
+      (grpc_ssl_server_credentials *)sc->base.server_creds;
+  tsi_result result = tsi_create_ssl_server_handshaker_factory_ex(
+      cert_pairs, config->num_key_cert_pairs, config->pem_root_certs,
+      get_tsi_client_certificate_request_type(
+          server_creds->config.client_certificate_request),
+      ssl_cipher_suites(), alpn_protocol_strings, (uint16_t)num_alpn_protocols,
+      &new_handshaker_factory);
+  gpr_free(cert_pairs);
+  gpr_free((void *)alpn_protocol_strings);
+
+  if (result != TSI_OK) {
+    gpr_log(GPR_ERROR, "Handshaker factory creation failed with %s.",
+            tsi_result_to_string(result));
+    return false;
+  }
+  tsi_ssl_server_handshaker_factory_unref(sc->server_handshaker_factory);
+  sc->server_handshaker_factory = new_handshaker_factory;
+  return true;
+}
+
+/* Attempts to fetch the server certificate config if a callback is available.
+ * Current certificate config will continue to be used if the callback returns
+ * an error. Returns true if new credentials were sucessfully loaded. */
+static bool try_fetch_ssl_server_credentials(
+    grpc_ssl_server_security_connector *sc) {
+  grpc_ssl_server_certificate_config *certificate_config = NULL;
+  bool status;
+
+  GPR_ASSERT(sc != NULL);
+  if (!server_connector_has_cert_config_fetcher(sc)) return false;
+
+  grpc_ssl_server_credentials *server_creds =
+      (grpc_ssl_server_credentials *)sc->base.server_creds;
+  grpc_ssl_certificate_config_reload_status cb_result =
+      server_creds->certificate_config_fetcher.cb(
+          server_creds->certificate_config_fetcher.user_data,
+          &certificate_config);
+  if (cb_result == GRPC_SSL_CERTIFICATE_CONFIG_RELOAD_UNCHANGED) {
+    gpr_log(GPR_DEBUG, "No change in SSL server credentials.");
+    status = false;
+  } else if (cb_result == GRPC_SSL_CERTIFICATE_CONFIG_RELOAD_NEW) {
+    status = try_replace_server_handshaker_factory(sc, certificate_config);
+  } else {
+    // Log error, continue using previously-loaded credentials.
+    gpr_log(GPR_ERROR,
+            "Failed fetching new server credentials, continuing to "
+            "use previously-loaded credentials.");
+    status = false;
+  }
+
+  if (certificate_config != NULL) {
+    grpc_ssl_server_certificate_config_destroy(certificate_config);
+  }
+  return status;
+}
+
 static void ssl_server_add_handshakers(grpc_exec_ctx *exec_ctx,
                                        grpc_server_security_connector *sc,
                                        grpc_handshake_manager *handshake_mgr) {
   grpc_ssl_server_security_connector *c =
       (grpc_ssl_server_security_connector *)sc;
   // Instantiate TSI handshaker.
+  try_fetch_ssl_server_credentials(c);
   tsi_handshaker *tsi_hs = NULL;
   tsi_result result = tsi_ssl_server_handshaker_factory_create_handshaker(
       c->server_handshaker_factory, &tsi_hs);
@@ -595,7 +718,6 @@ static void ssl_server_add_handshakers(grpc_exec_ctx *exec_ctx,
             tsi_result_to_string(result));
     return;
   }
-
   // Create handshakers.
   grpc_handshake_manager_add(
       handshake_mgr,
@@ -857,31 +979,6 @@ grpc_slice grpc_get_default_ssl_roots_for_testing(void) {
   return compute_default_pem_root_certs_once();
 }
 
-static tsi_client_certificate_request_type
-get_tsi_client_certificate_request_type(
-    grpc_ssl_client_certificate_request_type grpc_request_type) {
-  switch (grpc_request_type) {
-    case GRPC_SSL_DONT_REQUEST_CLIENT_CERTIFICATE:
-      return TSI_DONT_REQUEST_CLIENT_CERTIFICATE;
-
-    case GRPC_SSL_REQUEST_CLIENT_CERTIFICATE_BUT_DONT_VERIFY:
-      return TSI_REQUEST_CLIENT_CERTIFICATE_BUT_DONT_VERIFY;
-
-    case GRPC_SSL_REQUEST_CLIENT_CERTIFICATE_AND_VERIFY:
-      return TSI_REQUEST_CLIENT_CERTIFICATE_AND_VERIFY;
-
-    case GRPC_SSL_REQUEST_AND_REQUIRE_CLIENT_CERTIFICATE_BUT_DONT_VERIFY:
-      return TSI_REQUEST_AND_REQUIRE_CLIENT_CERTIFICATE_BUT_DONT_VERIFY;
-
-    case GRPC_SSL_REQUEST_AND_REQUIRE_CLIENT_CERTIFICATE_AND_VERIFY:
-      return TSI_REQUEST_AND_REQUIRE_CLIENT_CERTIFICATE_AND_VERIFY;
-
-    default:
-      // Is this a sane default
-      return TSI_DONT_REQUEST_CLIENT_CERTIFICATE;
-  }
-}
-
 const char *grpc_get_default_ssl_roots(void) {
   /* TODO(jboeuf@google.com): Maybe revisit the approach which consists in
      loading all the roots once for the lifetime of the process. */
@@ -897,18 +994,14 @@ grpc_security_status grpc_ssl_channel_security_connector_create(
     grpc_call_credentials *request_metadata_creds,
     const grpc_ssl_config *config, const char *target_name,
     const char *overridden_target_name, grpc_channel_security_connector **sc) {
-  size_t num_alpn_protocols = grpc_chttp2_num_alpn_versions();
+  size_t num_alpn_protocols = 0;
   const char **alpn_protocol_strings =
-      (const char **)gpr_malloc(sizeof(const char *) * num_alpn_protocols);
+      fill_alpn_protocol_strings(&num_alpn_protocols);
   tsi_result result = TSI_OK;
   grpc_ssl_channel_security_connector *c;
-  size_t i;
   const char *pem_root_certs;
   char *port;
   bool has_key_cert_pair;
-  for (i = 0; i < num_alpn_protocols; i++) {
-    alpn_protocol_strings[i] = grpc_chttp2_get_alpn_version_index(i);
-  }
 
   if (config == NULL || target_name == NULL) {
     gpr_log(GPR_ERROR, "An ssl channel needs a config and a target name.");
@@ -965,50 +1058,64 @@ error:
   return GRPC_SECURITY_ERROR;
 }
 
+static grpc_ssl_server_security_connector *
+grpc_ssl_server_security_connector_initialize(
+    grpc_server_credentials *server_creds) {
+  grpc_ssl_server_security_connector *c =
+      (grpc_ssl_server_security_connector *)gpr_zalloc(
+          sizeof(grpc_ssl_server_security_connector));
+  gpr_ref_init(&c->base.base.refcount, 1);
+  c->base.base.url_scheme = GRPC_SSL_URL_SCHEME;
+  c->base.base.vtable = &ssl_server_vtable;
+  c->base.add_handshakers = ssl_server_add_handshakers;
+  c->base.server_creds = grpc_server_credentials_ref(server_creds);
+  return c;
+}
+
 grpc_security_status grpc_ssl_server_security_connector_create(
-    grpc_exec_ctx *exec_ctx, grpc_server_credentials *server_creds,
-    const grpc_ssl_server_config *config, grpc_server_security_connector **sc) {
-  size_t num_alpn_protocols = grpc_chttp2_num_alpn_versions();
-  const char **alpn_protocol_strings =
-      (const char **)gpr_malloc(sizeof(const char *) * num_alpn_protocols);
+    grpc_exec_ctx *exec_ctx, grpc_server_credentials *gsc,
+    grpc_server_security_connector **sc) {
   tsi_result result = TSI_OK;
-  grpc_ssl_server_security_connector *c;
-  size_t i;
+  grpc_ssl_server_credentials *server_credentials =
+      (grpc_ssl_server_credentials *)gsc;
+  grpc_security_status retval = GRPC_SECURITY_OK;
 
-  for (i = 0; i < num_alpn_protocols; i++) {
-    alpn_protocol_strings[i] = grpc_chttp2_get_alpn_version_index(i);
-  }
+  GPR_ASSERT(server_credentials != NULL);
+  GPR_ASSERT(sc != NULL);
 
-  if (config == NULL || config->num_key_cert_pairs == 0) {
-    gpr_log(GPR_ERROR, "An SSL server needs a key and a cert.");
-    goto error;
+  grpc_ssl_server_security_connector *c =
+      grpc_ssl_server_security_connector_initialize(gsc);
+  if (server_connector_has_cert_config_fetcher(c)) {
+    // Load initial credentials from certificate_config_fetcher:
+    if (!try_fetch_ssl_server_credentials(c)) {
+      gpr_log(GPR_ERROR, "Failed loading SSL server credentials from fetcher.");
+      retval = GRPC_SECURITY_ERROR;
+    }
+  } else {
+    size_t num_alpn_protocols = 0;
+    const char **alpn_protocol_strings =
+        fill_alpn_protocol_strings(&num_alpn_protocols);
+    result = tsi_create_ssl_server_handshaker_factory_ex(
+        server_credentials->config.pem_key_cert_pairs,
+        server_credentials->config.num_key_cert_pairs,
+        server_credentials->config.pem_root_certs,
+        get_tsi_client_certificate_request_type(
+            server_credentials->config.client_certificate_request),
+        ssl_cipher_suites(), alpn_protocol_strings,
+        (uint16_t)num_alpn_protocols, &c->server_handshaker_factory);
+    gpr_free((void *)alpn_protocol_strings);
+    if (result != TSI_OK) {
+      gpr_log(GPR_ERROR, "Handshaker factory creation failed with %s.",
+              tsi_result_to_string(result));
+      retval = GRPC_SECURITY_ERROR;
+    }
   }
-  c = (grpc_ssl_server_security_connector *)gpr_zalloc(
-      sizeof(grpc_ssl_server_security_connector));
 
-  gpr_ref_init(&c->base.base.refcount, 1);
-  c->base.base.url_scheme = GRPC_SSL_URL_SCHEME;
-  c->base.base.vtable = &ssl_server_vtable;
-  c->base.server_creds = grpc_server_credentials_ref(server_creds);
-  result = tsi_create_ssl_server_handshaker_factory_ex(
-      config->pem_key_cert_pairs, config->num_key_cert_pairs,
-      config->pem_root_certs, get_tsi_client_certificate_request_type(
-                                  config->client_certificate_request),
-      ssl_cipher_suites(), alpn_protocol_strings, (uint16_t)num_alpn_protocols,
-      &c->server_handshaker_factory);
-  if (result != TSI_OK) {
-    gpr_log(GPR_ERROR, "Handshaker factory creation failed with %s.",
-            tsi_result_to_string(result));
-    ssl_server_destroy(exec_ctx, &c->base.base);
-    *sc = NULL;
-    goto error;
+  if (retval == GRPC_SECURITY_OK) {
+    *sc = &c->base;
+  } else {
+    if (c != NULL) ssl_server_destroy(exec_ctx, &c->base.base);
+    if (sc != NULL) *sc = NULL;
   }
-  c->base.add_handshakers = ssl_server_add_handshakers;
-  *sc = &c->base;
-  gpr_free((void *)alpn_protocol_strings);
-  return GRPC_SECURITY_OK;
-
-error:
-  gpr_free((void *)alpn_protocol_strings);
-  return GRPC_SECURITY_ERROR;
+  return retval;
 }
diff --git a/src/core/lib/security/transport/security_connector.h b/src/core/lib/security/transport/security_connector.h
index 8287151f44..54a563bb2c 100644
--- a/src/core/lib/security/transport/security_connector.h
+++ b/src/core/lib/security/transport/security_connector.h
@@ -248,8 +248,8 @@ typedef struct {
   specific error code otherwise.
 */
 grpc_security_status grpc_ssl_server_security_connector_create(
-    grpc_exec_ctx *exec_ctx, grpc_server_credentials *server_creds,
-    const grpc_ssl_server_config *config, grpc_server_security_connector **sc);
+    grpc_exec_ctx *exec_ctx, grpc_server_credentials *server_credentials,
+    grpc_server_security_connector **sc);
 
 /* Util. */
 const tsi_peer_property *tsi_peer_get_property_by_name(const tsi_peer *peer,
diff --git a/src/ruby/ext/grpc/rb_grpc_imports.generated.c b/src/ruby/ext/grpc/rb_grpc_imports.generated.c
index cd1bd98abc..843e05ab66 100644
--- a/src/ruby/ext/grpc/rb_grpc_imports.generated.c
+++ b/src/ruby/ext/grpc/rb_grpc_imports.generated.c
@@ -155,8 +155,14 @@ grpc_google_iam_credentials_create_type grpc_google_iam_credentials_create_impor
 grpc_metadata_credentials_create_from_plugin_type grpc_metadata_credentials_create_from_plugin_import;
 grpc_secure_channel_create_type grpc_secure_channel_create_import;
 grpc_server_credentials_release_type grpc_server_credentials_release_import;
+grpc_ssl_server_certificate_config_create_type grpc_ssl_server_certificate_config_create_import;
+grpc_ssl_server_certificate_config_destroy_type grpc_ssl_server_certificate_config_destroy_import;
 grpc_ssl_server_credentials_create_type grpc_ssl_server_credentials_create_import;
 grpc_ssl_server_credentials_create_ex_type grpc_ssl_server_credentials_create_ex_import;
+grpc_ssl_server_credentials_create_options_using_config_type grpc_ssl_server_credentials_create_options_using_config_import;
+grpc_ssl_server_credentials_create_options_using_config_fetcher_type grpc_ssl_server_credentials_create_options_using_config_fetcher_import;
+grpc_ssl_server_credentials_options_destroy_type grpc_ssl_server_credentials_options_destroy_import;
+grpc_ssl_server_credentials_create_with_options_type grpc_ssl_server_credentials_create_with_options_import;
 grpc_server_add_secure_http2_port_type grpc_server_add_secure_http2_port_import;
 grpc_call_set_credentials_type grpc_call_set_credentials_import;
 grpc_server_credentials_set_auth_metadata_processor_type grpc_server_credentials_set_auth_metadata_processor_import;
@@ -465,8 +471,14 @@ void grpc_rb_load_imports(HMODULE library) {
   grpc_metadata_credentials_create_from_plugin_import = (grpc_metadata_credentials_create_from_plugin_type) GetProcAddress(library, "grpc_metadata_credentials_create_from_plugin");
   grpc_secure_channel_create_import = (grpc_secure_channel_create_type) GetProcAddress(library, "grpc_secure_channel_create");
   grpc_server_credentials_release_import = (grpc_server_credentials_release_type) GetProcAddress(library, "grpc_server_credentials_release");
+  grpc_ssl_server_certificate_config_create_import = (grpc_ssl_server_certificate_config_create_type) GetProcAddress(library, "grpc_ssl_server_certificate_config_create");
+  grpc_ssl_server_certificate_config_destroy_import = (grpc_ssl_server_certificate_config_destroy_type) GetProcAddress(library, "grpc_ssl_server_certificate_config_destroy");
   grpc_ssl_server_credentials_create_import = (grpc_ssl_server_credentials_create_type) GetProcAddress(library, "grpc_ssl_server_credentials_create");
   grpc_ssl_server_credentials_create_ex_import = (grpc_ssl_server_credentials_create_ex_type) GetProcAddress(library, "grpc_ssl_server_credentials_create_ex");
+  grpc_ssl_server_credentials_create_options_using_config_import = (grpc_ssl_server_credentials_create_options_using_config_type) GetProcAddress(library, "grpc_ssl_server_credentials_create_options_using_config");
+  grpc_ssl_server_credentials_create_options_using_config_fetcher_import = (grpc_ssl_server_credentials_create_options_using_config_fetcher_type) GetProcAddress(library, "grpc_ssl_server_credentials_create_options_using_config_fetcher");
+  grpc_ssl_server_credentials_options_destroy_import = (grpc_ssl_server_credentials_options_destroy_type) GetProcAddress(library, "grpc_ssl_server_credentials_options_destroy");
+  grpc_ssl_server_credentials_create_with_options_import = (grpc_ssl_server_credentials_create_with_options_type) GetProcAddress(library, "grpc_ssl_server_credentials_create_with_options");
   grpc_server_add_secure_http2_port_import = (grpc_server_add_secure_http2_port_type) GetProcAddress(library, "grpc_server_add_secure_http2_port");
   grpc_call_set_credentials_import = (grpc_call_set_credentials_type) GetProcAddress(library, "grpc_call_set_credentials");
   grpc_server_credentials_set_auth_metadata_processor_import = (grpc_server_credentials_set_auth_metadata_processor_type) GetProcAddress(library, "grpc_server_credentials_set_auth_metadata_processor");
diff --git a/src/ruby/ext/grpc/rb_grpc_imports.generated.h b/src/ruby/ext/grpc/rb_grpc_imports.generated.h
index c7e78b70dc..998eef800e 100644
--- a/src/ruby/ext/grpc/rb_grpc_imports.generated.h
+++ b/src/ruby/ext/grpc/rb_grpc_imports.generated.h
@@ -446,12 +446,30 @@ extern grpc_secure_channel_create_type grpc_secure_channel_create_import;
 typedef void(*grpc_server_credentials_release_type)(grpc_server_credentials *creds);
 extern grpc_server_credentials_release_type grpc_server_credentials_release_import;
 #define grpc_server_credentials_release grpc_server_credentials_release_import
+typedef grpc_ssl_server_certificate_config *(*grpc_ssl_server_certificate_config_create_type)(const char *pem_root_certs, const grpc_ssl_pem_key_cert_pair *pem_key_cert_pairs, size_t num_key_cert_pairs);
+extern grpc_ssl_server_certificate_config_create_type grpc_ssl_server_certificate_config_create_import;
+#define grpc_ssl_server_certificate_config_create grpc_ssl_server_certificate_config_create_import
+typedef void(*grpc_ssl_server_certificate_config_destroy_type)(grpc_ssl_server_certificate_config *config);
+extern grpc_ssl_server_certificate_config_destroy_type grpc_ssl_server_certificate_config_destroy_import;
+#define grpc_ssl_server_certificate_config_destroy grpc_ssl_server_certificate_config_destroy_import
 typedef grpc_server_credentials *(*grpc_ssl_server_credentials_create_type)(const char *pem_root_certs, grpc_ssl_pem_key_cert_pair *pem_key_cert_pairs, size_t num_key_cert_pairs, int force_client_auth, void *reserved);
 extern grpc_ssl_server_credentials_create_type grpc_ssl_server_credentials_create_import;
 #define grpc_ssl_server_credentials_create grpc_ssl_server_credentials_create_import
 typedef grpc_server_credentials *(*grpc_ssl_server_credentials_create_ex_type)(const char *pem_root_certs, grpc_ssl_pem_key_cert_pair *pem_key_cert_pairs, size_t num_key_cert_pairs, grpc_ssl_client_certificate_request_type client_certificate_request, void *reserved);
 extern grpc_ssl_server_credentials_create_ex_type grpc_ssl_server_credentials_create_ex_import;
 #define grpc_ssl_server_credentials_create_ex grpc_ssl_server_credentials_create_ex_import
+typedef grpc_ssl_server_credentials_options *(*grpc_ssl_server_credentials_create_options_using_config_type)(grpc_ssl_client_certificate_request_type client_certificate_request, grpc_ssl_server_certificate_config *certificate_config);
+extern grpc_ssl_server_credentials_create_options_using_config_type grpc_ssl_server_credentials_create_options_using_config_import;
+#define grpc_ssl_server_credentials_create_options_using_config grpc_ssl_server_credentials_create_options_using_config_import
+typedef grpc_ssl_server_credentials_options *(*grpc_ssl_server_credentials_create_options_using_config_fetcher_type)(grpc_ssl_client_certificate_request_type client_certificate_request, grpc_ssl_server_certificate_config_callback cb, void *user_data);
+extern grpc_ssl_server_credentials_create_options_using_config_fetcher_type grpc_ssl_server_credentials_create_options_using_config_fetcher_import;
+#define grpc_ssl_server_credentials_create_options_using_config_fetcher grpc_ssl_server_credentials_create_options_using_config_fetcher_import
+typedef void(*grpc_ssl_server_credentials_options_destroy_type)(grpc_ssl_server_credentials_options *options);
+extern grpc_ssl_server_credentials_options_destroy_type grpc_ssl_server_credentials_options_destroy_import;
+#define grpc_ssl_server_credentials_options_destroy grpc_ssl_server_credentials_options_destroy_import
+typedef grpc_server_credentials *(*grpc_ssl_server_credentials_create_with_options_type)(grpc_ssl_server_credentials_options *options);
+extern grpc_ssl_server_credentials_create_with_options_type grpc_ssl_server_credentials_create_with_options_import;
+#define grpc_ssl_server_credentials_create_with_options grpc_ssl_server_credentials_create_with_options_import
 typedef int(*grpc_server_add_secure_http2_port_type)(grpc_server *server, const char *addr, grpc_server_credentials *creds);
 extern grpc_server_add_secure_http2_port_type grpc_server_add_secure_http2_port_import;
 #define grpc_server_add_secure_http2_port grpc_server_add_secure_http2_port_import
-- 
cgit v1.2.3


From 2f921a3f9727859d36c77c8af61c84877e73e880 Mon Sep 17 00:00:00 2001
From: Yash Tibrewal <yashkt@google.com>
Date: Thu, 2 Nov 2017 16:59:19 -0700
Subject: Remove grpc_slice_buf_cmp and change public_headers_must_be_c89 to
 print the addresses of public api functions to check if they can be linked to

---
 grpc.def                                           |   1 -
 include/grpc/slice.h                               |   1 -
 src/ruby/ext/grpc/rb_grpc_imports.generated.c      |   2 -
 src/ruby/ext/grpc/rb_grpc_imports.generated.h      |   3 -
 .../surface/public_headers_must_be_c89.c.template  |  16 +-
 test/core/surface/public_headers_must_be_c89.c     | 583 ++++++++++++++++++++-
 6 files changed, 597 insertions(+), 9 deletions(-)

(limited to 'grpc.def')

diff --git a/grpc.def b/grpc.def
index 710be52a3b..07c0b3e928 100644
--- a/grpc.def
+++ b/grpc.def
@@ -149,7 +149,6 @@ EXPORTS
     grpc_slice_eq
     grpc_slice_cmp
     grpc_slice_str_cmp
-    grpc_slice_buf_cmp
     grpc_slice_buf_start_eq
     grpc_slice_rchr
     grpc_slice_chr
diff --git a/include/grpc/slice.h b/include/grpc/slice.h
index 3f3cff1408..2203e3887b 100644
--- a/include/grpc/slice.h
+++ b/include/grpc/slice.h
@@ -137,7 +137,6 @@ GPRAPI int grpc_slice_eq(grpc_slice a, grpc_slice b);
    versions of the API. */
 GPRAPI int grpc_slice_cmp(grpc_slice a, grpc_slice b);
 GPRAPI int grpc_slice_str_cmp(grpc_slice a, const char *b);
-GPRAPI int grpc_slice_buf_cmp(grpc_slice a, const void *b, size_t blen);
 
 /** return non-zero if the first blen bytes of a are equal to b */
 GPRAPI int grpc_slice_buf_start_eq(grpc_slice a, const void *b, size_t blen);
diff --git a/src/ruby/ext/grpc/rb_grpc_imports.generated.c b/src/ruby/ext/grpc/rb_grpc_imports.generated.c
index 128e912e23..648d515003 100644
--- a/src/ruby/ext/grpc/rb_grpc_imports.generated.c
+++ b/src/ruby/ext/grpc/rb_grpc_imports.generated.c
@@ -172,7 +172,6 @@ grpc_slice_default_eq_impl_type grpc_slice_default_eq_impl_import;
 grpc_slice_eq_type grpc_slice_eq_import;
 grpc_slice_cmp_type grpc_slice_cmp_import;
 grpc_slice_str_cmp_type grpc_slice_str_cmp_import;
-grpc_slice_buf_cmp_type grpc_slice_buf_cmp_import;
 grpc_slice_buf_start_eq_type grpc_slice_buf_start_eq_import;
 grpc_slice_rchr_type grpc_slice_rchr_import;
 grpc_slice_chr_type grpc_slice_chr_import;
@@ -460,7 +459,6 @@ void grpc_rb_load_imports(HMODULE library) {
   grpc_slice_eq_import = (grpc_slice_eq_type) GetProcAddress(library, "grpc_slice_eq");
   grpc_slice_cmp_import = (grpc_slice_cmp_type) GetProcAddress(library, "grpc_slice_cmp");
   grpc_slice_str_cmp_import = (grpc_slice_str_cmp_type) GetProcAddress(library, "grpc_slice_str_cmp");
-  grpc_slice_buf_cmp_import = (grpc_slice_buf_cmp_type) GetProcAddress(library, "grpc_slice_buf_cmp");
   grpc_slice_buf_start_eq_import = (grpc_slice_buf_start_eq_type) GetProcAddress(library, "grpc_slice_buf_start_eq");
   grpc_slice_rchr_import = (grpc_slice_rchr_type) GetProcAddress(library, "grpc_slice_rchr");
   grpc_slice_chr_import = (grpc_slice_chr_type) GetProcAddress(library, "grpc_slice_chr");
diff --git a/src/ruby/ext/grpc/rb_grpc_imports.generated.h b/src/ruby/ext/grpc/rb_grpc_imports.generated.h
index b9b82e5e23..4f2383e982 100644
--- a/src/ruby/ext/grpc/rb_grpc_imports.generated.h
+++ b/src/ruby/ext/grpc/rb_grpc_imports.generated.h
@@ -496,9 +496,6 @@ extern grpc_slice_cmp_type grpc_slice_cmp_import;
 typedef int(*grpc_slice_str_cmp_type)(grpc_slice a, const char *b);
 extern grpc_slice_str_cmp_type grpc_slice_str_cmp_import;
 #define grpc_slice_str_cmp grpc_slice_str_cmp_import
-typedef int(*grpc_slice_buf_cmp_type)(grpc_slice a, const void *b, size_t blen);
-extern grpc_slice_buf_cmp_type grpc_slice_buf_cmp_import;
-#define grpc_slice_buf_cmp grpc_slice_buf_cmp_import
 typedef int(*grpc_slice_buf_start_eq_type)(grpc_slice a, const void *b, size_t blen);
 extern grpc_slice_buf_start_eq_type grpc_slice_buf_start_eq_import;
 #define grpc_slice_buf_start_eq grpc_slice_buf_start_eq_import
diff --git a/templates/test/core/surface/public_headers_must_be_c89.c.template b/templates/test/core/surface/public_headers_must_be_c89.c.template
index dcaa59bb30..d6a1a8ceff 100644
--- a/templates/test/core/surface/public_headers_must_be_c89.c.template
+++ b/templates/test/core/surface/public_headers_must_be_c89.c.template
@@ -35,9 +35,23 @@
       assert(hdr[0:len(pfx)] == pfx)
       hdrs.add(hdr[len(pfx):])
   hdrs = sorted(list(hdrs))
+  fns = list()
+  for api in c_apis:
+    if is_platform_header(api.header):
+      continue
+    fns.append(api.name)
   %>\
   % for hdr in hdrs:
   #include <${hdr}>
   % endfor
 
-  int main(int argc, char **argv) { return 0; }
+  #include <stdio.h>
+
+  int main(int argc, char **argv) {
+    if(argc == 12345678) {
+      % for fn in fns:
+      printf("%lx", (unsigned long) ${fn});;
+      % endfor
+    }
+    return 0;
+  }
diff --git a/test/core/surface/public_headers_must_be_c89.c b/test/core/surface/public_headers_must_be_c89.c
index d36d116afb..280b6c423c 100644
--- a/test/core/surface/public_headers_must_be_c89.c
+++ b/test/core/surface/public_headers_must_be_c89.c
@@ -63,4 +63,585 @@
 #include <grpc/support/useful.h>
 #include <grpc/support/workaround_list.h>
 
-int main(int argc, char **argv) { return 0; }
+#include <stdio.h>
+
+int main(int argc, char **argv) {
+  if (argc == 12345678) {
+    printf("%lx", (unsigned long)grpc_compression_algorithm_parse);
+    ;
+    printf("%lx", (unsigned long)grpc_compression_algorithm_name);
+    ;
+    printf("%lx", (unsigned long)grpc_stream_compression_algorithm_name);
+    ;
+    printf("%lx", (unsigned long)grpc_compression_algorithm_for_level);
+    ;
+    printf("%lx", (unsigned long)grpc_stream_compression_algorithm_for_level);
+    ;
+    printf("%lx", (unsigned long)grpc_compression_options_init);
+    ;
+    printf("%lx", (unsigned long)grpc_compression_options_enable_algorithm);
+    ;
+    printf("%lx", (unsigned long)grpc_compression_options_disable_algorithm);
+    ;
+    printf("%lx", (unsigned long)grpc_compression_options_is_algorithm_enabled);
+    ;
+    printf(
+        "%lx",
+        (unsigned long)
+            grpc_compression_options_is_stream_compression_algorithm_enabled);
+    ;
+    printf("%lx", (unsigned long)grpc_metadata_array_init);
+    ;
+    printf("%lx", (unsigned long)grpc_metadata_array_destroy);
+    ;
+    printf("%lx", (unsigned long)grpc_call_details_init);
+    ;
+    printf("%lx", (unsigned long)grpc_call_details_destroy);
+    ;
+    printf("%lx", (unsigned long)grpc_register_plugin);
+    ;
+    printf("%lx", (unsigned long)grpc_init);
+    ;
+    printf("%lx", (unsigned long)grpc_shutdown);
+    ;
+    printf("%lx", (unsigned long)grpc_version_string);
+    ;
+    printf("%lx", (unsigned long)grpc_g_stands_for);
+    ;
+    printf("%lx", (unsigned long)grpc_completion_queue_factory_lookup);
+    ;
+    printf("%lx", (unsigned long)grpc_completion_queue_create_for_next);
+    ;
+    printf("%lx", (unsigned long)grpc_completion_queue_create_for_pluck);
+    ;
+    printf("%lx", (unsigned long)grpc_completion_queue_create);
+    ;
+    printf("%lx", (unsigned long)grpc_completion_queue_next);
+    ;
+    printf("%lx", (unsigned long)grpc_completion_queue_pluck);
+    ;
+    printf("%lx", (unsigned long)grpc_completion_queue_shutdown);
+    ;
+    printf("%lx", (unsigned long)grpc_completion_queue_destroy);
+    ;
+    printf("%lx", (unsigned long)grpc_completion_queue_thread_local_cache_init);
+    ;
+    printf("%lx",
+           (unsigned long)grpc_completion_queue_thread_local_cache_flush);
+    ;
+    printf("%lx", (unsigned long)grpc_alarm_create);
+    ;
+    printf("%lx", (unsigned long)grpc_alarm_set);
+    ;
+    printf("%lx", (unsigned long)grpc_alarm_cancel);
+    ;
+    printf("%lx", (unsigned long)grpc_alarm_destroy);
+    ;
+    printf("%lx", (unsigned long)grpc_channel_check_connectivity_state);
+    ;
+    printf("%lx",
+           (unsigned long)grpc_channel_num_external_connectivity_watchers);
+    ;
+    printf("%lx", (unsigned long)grpc_channel_watch_connectivity_state);
+    ;
+    printf("%lx", (unsigned long)grpc_channel_support_connectivity_watcher);
+    ;
+    printf("%lx", (unsigned long)grpc_channel_create_call);
+    ;
+    printf("%lx", (unsigned long)grpc_channel_ping);
+    ;
+    printf("%lx", (unsigned long)grpc_channel_register_call);
+    ;
+    printf("%lx", (unsigned long)grpc_channel_create_registered_call);
+    ;
+    printf("%lx", (unsigned long)grpc_call_arena_alloc);
+    ;
+    printf("%lx", (unsigned long)grpc_call_start_batch);
+    ;
+    printf("%lx", (unsigned long)grpc_call_get_peer);
+    ;
+    printf("%lx", (unsigned long)grpc_census_call_set_context);
+    ;
+    printf("%lx", (unsigned long)grpc_census_call_get_context);
+    ;
+    printf("%lx", (unsigned long)grpc_channel_get_target);
+    ;
+    printf("%lx", (unsigned long)grpc_channel_get_info);
+    ;
+    printf("%lx", (unsigned long)grpc_insecure_channel_create);
+    ;
+    printf("%lx", (unsigned long)grpc_lame_client_channel_create);
+    ;
+    printf("%lx", (unsigned long)grpc_channel_destroy);
+    ;
+    printf("%lx", (unsigned long)grpc_call_cancel);
+    ;
+    printf("%lx", (unsigned long)grpc_call_cancel_with_status);
+    ;
+    printf("%lx", (unsigned long)grpc_call_ref);
+    ;
+    printf("%lx", (unsigned long)grpc_call_unref);
+    ;
+    printf("%lx", (unsigned long)grpc_server_request_call);
+    ;
+    printf("%lx", (unsigned long)grpc_server_register_method);
+    ;
+    printf("%lx", (unsigned long)grpc_server_request_registered_call);
+    ;
+    printf("%lx", (unsigned long)grpc_server_create);
+    ;
+    printf("%lx", (unsigned long)grpc_server_register_completion_queue);
+    ;
+    printf("%lx", (unsigned long)grpc_server_add_insecure_http2_port);
+    ;
+    printf("%lx", (unsigned long)grpc_server_start);
+    ;
+    printf("%lx", (unsigned long)grpc_server_shutdown_and_notify);
+    ;
+    printf("%lx", (unsigned long)grpc_server_cancel_all_calls);
+    ;
+    printf("%lx", (unsigned long)grpc_server_destroy);
+    ;
+    printf("%lx", (unsigned long)grpc_tracer_set_enabled);
+    ;
+    printf("%lx", (unsigned long)grpc_header_key_is_legal);
+    ;
+    printf("%lx", (unsigned long)grpc_header_nonbin_value_is_legal);
+    ;
+    printf("%lx", (unsigned long)grpc_is_binary_header);
+    ;
+    printf("%lx", (unsigned long)grpc_call_error_to_string);
+    ;
+    printf("%lx", (unsigned long)grpc_resource_quota_create);
+    ;
+    printf("%lx", (unsigned long)grpc_resource_quota_ref);
+    ;
+    printf("%lx", (unsigned long)grpc_resource_quota_unref);
+    ;
+    printf("%lx", (unsigned long)grpc_resource_quota_resize);
+    ;
+    printf("%lx", (unsigned long)grpc_resource_quota_arg_vtable);
+    ;
+    printf("%lx", (unsigned long)grpc_auth_property_iterator_next);
+    ;
+    printf("%lx", (unsigned long)grpc_auth_context_property_iterator);
+    ;
+    printf("%lx", (unsigned long)grpc_auth_context_peer_identity);
+    ;
+    printf("%lx", (unsigned long)grpc_auth_context_find_properties_by_name);
+    ;
+    printf("%lx", (unsigned long)grpc_auth_context_peer_identity_property_name);
+    ;
+    printf("%lx", (unsigned long)grpc_auth_context_peer_is_authenticated);
+    ;
+    printf("%lx", (unsigned long)grpc_call_auth_context);
+    ;
+    printf("%lx", (unsigned long)grpc_auth_context_release);
+    ;
+    printf("%lx", (unsigned long)grpc_auth_context_add_property);
+    ;
+    printf("%lx", (unsigned long)grpc_auth_context_add_cstring_property);
+    ;
+    printf("%lx",
+           (unsigned long)grpc_auth_context_set_peer_identity_property_name);
+    ;
+    printf("%lx", (unsigned long)grpc_channel_credentials_release);
+    ;
+    printf("%lx", (unsigned long)grpc_google_default_credentials_create);
+    ;
+    printf("%lx", (unsigned long)grpc_set_ssl_roots_override_callback);
+    ;
+    printf("%lx", (unsigned long)grpc_ssl_credentials_create);
+    ;
+    printf("%lx", (unsigned long)grpc_call_credentials_release);
+    ;
+    printf("%lx", (unsigned long)grpc_composite_channel_credentials_create);
+    ;
+    printf("%lx", (unsigned long)grpc_composite_call_credentials_create);
+    ;
+    printf("%lx", (unsigned long)grpc_google_compute_engine_credentials_create);
+    ;
+    printf("%lx", (unsigned long)grpc_max_auth_token_lifetime);
+    ;
+    printf("%lx",
+           (unsigned long)grpc_service_account_jwt_access_credentials_create);
+    ;
+    printf("%lx", (unsigned long)grpc_google_refresh_token_credentials_create);
+    ;
+    printf("%lx", (unsigned long)grpc_access_token_credentials_create);
+    ;
+    printf("%lx", (unsigned long)grpc_google_iam_credentials_create);
+    ;
+    printf("%lx", (unsigned long)grpc_metadata_credentials_create_from_plugin);
+    ;
+    printf("%lx", (unsigned long)grpc_secure_channel_create);
+    ;
+    printf("%lx", (unsigned long)grpc_server_credentials_release);
+    ;
+    printf("%lx", (unsigned long)grpc_ssl_server_certificate_config_create);
+    ;
+    printf("%lx", (unsigned long)grpc_ssl_server_certificate_config_destroy);
+    ;
+    printf("%lx", (unsigned long)grpc_ssl_server_credentials_create);
+    ;
+    printf("%lx", (unsigned long)grpc_ssl_server_credentials_create_ex);
+    ;
+    printf(
+        "%lx",
+        (unsigned long)grpc_ssl_server_credentials_create_options_using_config);
+    ;
+    printf("%lx",
+           (unsigned long)
+               grpc_ssl_server_credentials_create_options_using_config_fetcher);
+    ;
+    printf("%lx", (unsigned long)grpc_ssl_server_credentials_options_destroy);
+    ;
+    printf("%lx",
+           (unsigned long)grpc_ssl_server_credentials_create_with_options);
+    ;
+    printf("%lx", (unsigned long)grpc_server_add_secure_http2_port);
+    ;
+    printf("%lx", (unsigned long)grpc_call_set_credentials);
+    ;
+    printf("%lx",
+           (unsigned long)grpc_server_credentials_set_auth_metadata_processor);
+    ;
+    printf("%lx", (unsigned long)grpc_raw_byte_buffer_create);
+    ;
+    printf("%lx", (unsigned long)grpc_raw_compressed_byte_buffer_create);
+    ;
+    printf("%lx", (unsigned long)grpc_byte_buffer_copy);
+    ;
+    printf("%lx", (unsigned long)grpc_byte_buffer_length);
+    ;
+    printf("%lx", (unsigned long)grpc_byte_buffer_destroy);
+    ;
+    printf("%lx", (unsigned long)grpc_byte_buffer_reader_init);
+    ;
+    printf("%lx", (unsigned long)grpc_byte_buffer_reader_destroy);
+    ;
+    printf("%lx", (unsigned long)grpc_byte_buffer_reader_next);
+    ;
+    printf("%lx", (unsigned long)grpc_byte_buffer_reader_readall);
+    ;
+    printf("%lx", (unsigned long)grpc_raw_byte_buffer_from_reader);
+    ;
+    printf("%lx", (unsigned long)grpc_slice_ref);
+    ;
+    printf("%lx", (unsigned long)grpc_slice_unref);
+    ;
+    printf("%lx", (unsigned long)grpc_slice_copy);
+    ;
+    printf("%lx", (unsigned long)grpc_slice_new);
+    ;
+    printf("%lx", (unsigned long)grpc_slice_new_with_user_data);
+    ;
+    printf("%lx", (unsigned long)grpc_slice_new_with_len);
+    ;
+    printf("%lx", (unsigned long)grpc_slice_malloc);
+    ;
+    printf("%lx", (unsigned long)grpc_slice_malloc_large);
+    ;
+    printf("%lx", (unsigned long)grpc_slice_intern);
+    ;
+    printf("%lx", (unsigned long)grpc_slice_from_copied_string);
+    ;
+    printf("%lx", (unsigned long)grpc_slice_from_copied_buffer);
+    ;
+    printf("%lx", (unsigned long)grpc_slice_from_static_string);
+    ;
+    printf("%lx", (unsigned long)grpc_slice_from_static_buffer);
+    ;
+    printf("%lx", (unsigned long)grpc_slice_sub);
+    ;
+    printf("%lx", (unsigned long)grpc_slice_sub_no_ref);
+    ;
+    printf("%lx", (unsigned long)grpc_slice_split_tail);
+    ;
+    printf("%lx", (unsigned long)grpc_slice_split_tail_maybe_ref);
+    ;
+    printf("%lx", (unsigned long)grpc_slice_split_head);
+    ;
+    printf("%lx", (unsigned long)grpc_empty_slice);
+    ;
+    printf("%lx", (unsigned long)grpc_slice_default_hash_impl);
+    ;
+    printf("%lx", (unsigned long)grpc_slice_default_eq_impl);
+    ;
+    printf("%lx", (unsigned long)grpc_slice_eq);
+    ;
+    printf("%lx", (unsigned long)grpc_slice_cmp);
+    ;
+    printf("%lx", (unsigned long)grpc_slice_str_cmp);
+    ;
+    printf("%lx", (unsigned long)grpc_slice_buf_start_eq);
+    ;
+    printf("%lx", (unsigned long)grpc_slice_rchr);
+    ;
+    printf("%lx", (unsigned long)grpc_slice_chr);
+    ;
+    printf("%lx", (unsigned long)grpc_slice_slice);
+    ;
+    printf("%lx", (unsigned long)grpc_slice_hash);
+    ;
+    printf("%lx", (unsigned long)grpc_slice_is_equivalent);
+    ;
+    printf("%lx", (unsigned long)grpc_slice_dup);
+    ;
+    printf("%lx", (unsigned long)grpc_slice_to_c_string);
+    ;
+    printf("%lx", (unsigned long)grpc_slice_buffer_init);
+    ;
+    printf("%lx", (unsigned long)grpc_slice_buffer_destroy);
+    ;
+    printf("%lx", (unsigned long)grpc_slice_buffer_add);
+    ;
+    printf("%lx", (unsigned long)grpc_slice_buffer_add_indexed);
+    ;
+    printf("%lx", (unsigned long)grpc_slice_buffer_addn);
+    ;
+    printf("%lx", (unsigned long)grpc_slice_buffer_tiny_add);
+    ;
+    printf("%lx", (unsigned long)grpc_slice_buffer_pop);
+    ;
+    printf("%lx", (unsigned long)grpc_slice_buffer_reset_and_unref);
+    ;
+    printf("%lx", (unsigned long)grpc_slice_buffer_swap);
+    ;
+    printf("%lx", (unsigned long)grpc_slice_buffer_move_into);
+    ;
+    printf("%lx", (unsigned long)grpc_slice_buffer_trim_end);
+    ;
+    printf("%lx", (unsigned long)grpc_slice_buffer_move_first);
+    ;
+    printf("%lx", (unsigned long)grpc_slice_buffer_move_first_no_ref);
+    ;
+    printf("%lx", (unsigned long)grpc_slice_buffer_move_first_into_buffer);
+    ;
+    printf("%lx", (unsigned long)grpc_slice_buffer_take_first);
+    ;
+    printf("%lx", (unsigned long)grpc_slice_buffer_undo_take_first);
+    ;
+    printf("%lx", (unsigned long)gpr_malloc);
+    ;
+    printf("%lx", (unsigned long)gpr_zalloc);
+    ;
+    printf("%lx", (unsigned long)gpr_free);
+    ;
+    printf("%lx", (unsigned long)gpr_realloc);
+    ;
+    printf("%lx", (unsigned long)gpr_malloc_aligned);
+    ;
+    printf("%lx", (unsigned long)gpr_free_aligned);
+    ;
+    printf("%lx", (unsigned long)gpr_set_allocation_functions);
+    ;
+    printf("%lx", (unsigned long)gpr_get_allocation_functions);
+    ;
+    printf("%lx", (unsigned long)gpr_avl_create);
+    ;
+    printf("%lx", (unsigned long)gpr_avl_ref);
+    ;
+    printf("%lx", (unsigned long)gpr_avl_unref);
+    ;
+    printf("%lx", (unsigned long)gpr_avl_add);
+    ;
+    printf("%lx", (unsigned long)gpr_avl_remove);
+    ;
+    printf("%lx", (unsigned long)gpr_avl_get);
+    ;
+    printf("%lx", (unsigned long)gpr_avl_maybe_get);
+    ;
+    printf("%lx", (unsigned long)gpr_avl_is_empty);
+    ;
+    printf("%lx", (unsigned long)gpr_cmdline_create);
+    ;
+    printf("%lx", (unsigned long)gpr_cmdline_add_int);
+    ;
+    printf("%lx", (unsigned long)gpr_cmdline_add_flag);
+    ;
+    printf("%lx", (unsigned long)gpr_cmdline_add_string);
+    ;
+    printf("%lx", (unsigned long)gpr_cmdline_on_extra_arg);
+    ;
+    printf("%lx", (unsigned long)gpr_cmdline_set_survive_failure);
+    ;
+    printf("%lx", (unsigned long)gpr_cmdline_parse);
+    ;
+    printf("%lx", (unsigned long)gpr_cmdline_destroy);
+    ;
+    printf("%lx", (unsigned long)gpr_cmdline_usage_string);
+    ;
+    printf("%lx", (unsigned long)gpr_cpu_num_cores);
+    ;
+    printf("%lx", (unsigned long)gpr_cpu_current_cpu);
+    ;
+    printf("%lx", (unsigned long)gpr_histogram_create);
+    ;
+    printf("%lx", (unsigned long)gpr_histogram_destroy);
+    ;
+    printf("%lx", (unsigned long)gpr_histogram_add);
+    ;
+    printf("%lx", (unsigned long)gpr_histogram_merge);
+    ;
+    printf("%lx", (unsigned long)gpr_histogram_percentile);
+    ;
+    printf("%lx", (unsigned long)gpr_histogram_mean);
+    ;
+    printf("%lx", (unsigned long)gpr_histogram_stddev);
+    ;
+    printf("%lx", (unsigned long)gpr_histogram_variance);
+    ;
+    printf("%lx", (unsigned long)gpr_histogram_maximum);
+    ;
+    printf("%lx", (unsigned long)gpr_histogram_minimum);
+    ;
+    printf("%lx", (unsigned long)gpr_histogram_count);
+    ;
+    printf("%lx", (unsigned long)gpr_histogram_sum);
+    ;
+    printf("%lx", (unsigned long)gpr_histogram_sum_of_squares);
+    ;
+    printf("%lx", (unsigned long)gpr_histogram_get_contents);
+    ;
+    printf("%lx", (unsigned long)gpr_histogram_merge_contents);
+    ;
+    printf("%lx", (unsigned long)gpr_join_host_port);
+    ;
+    printf("%lx", (unsigned long)gpr_split_host_port);
+    ;
+    printf("%lx", (unsigned long)gpr_log_severity_string);
+    ;
+    printf("%lx", (unsigned long)gpr_log);
+    ;
+    printf("%lx", (unsigned long)gpr_log_message);
+    ;
+    printf("%lx", (unsigned long)gpr_set_log_verbosity);
+    ;
+    printf("%lx", (unsigned long)gpr_log_verbosity_init);
+    ;
+    printf("%lx", (unsigned long)gpr_set_log_function);
+    ;
+    printf("%lx", (unsigned long)gpr_strdup);
+    ;
+    printf("%lx", (unsigned long)gpr_asprintf);
+    ;
+    printf("%lx", (unsigned long)gpr_subprocess_binary_extension);
+    ;
+    printf("%lx", (unsigned long)gpr_subprocess_create);
+    ;
+    printf("%lx", (unsigned long)gpr_subprocess_destroy);
+    ;
+    printf("%lx", (unsigned long)gpr_subprocess_join);
+    ;
+    printf("%lx", (unsigned long)gpr_subprocess_interrupt);
+    ;
+    printf("%lx", (unsigned long)gpr_mu_init);
+    ;
+    printf("%lx", (unsigned long)gpr_mu_destroy);
+    ;
+    printf("%lx", (unsigned long)gpr_mu_lock);
+    ;
+    printf("%lx", (unsigned long)gpr_mu_unlock);
+    ;
+    printf("%lx", (unsigned long)gpr_mu_trylock);
+    ;
+    printf("%lx", (unsigned long)gpr_cv_init);
+    ;
+    printf("%lx", (unsigned long)gpr_cv_destroy);
+    ;
+    printf("%lx", (unsigned long)gpr_cv_wait);
+    ;
+    printf("%lx", (unsigned long)gpr_cv_signal);
+    ;
+    printf("%lx", (unsigned long)gpr_cv_broadcast);
+    ;
+    printf("%lx", (unsigned long)gpr_once_init);
+    ;
+    printf("%lx", (unsigned long)gpr_event_init);
+    ;
+    printf("%lx", (unsigned long)gpr_event_set);
+    ;
+    printf("%lx", (unsigned long)gpr_event_get);
+    ;
+    printf("%lx", (unsigned long)gpr_event_wait);
+    ;
+    printf("%lx", (unsigned long)gpr_ref_init);
+    ;
+    printf("%lx", (unsigned long)gpr_ref);
+    ;
+    printf("%lx", (unsigned long)gpr_ref_non_zero);
+    ;
+    printf("%lx", (unsigned long)gpr_refn);
+    ;
+    printf("%lx", (unsigned long)gpr_unref);
+    ;
+    printf("%lx", (unsigned long)gpr_ref_is_unique);
+    ;
+    printf("%lx", (unsigned long)gpr_stats_init);
+    ;
+    printf("%lx", (unsigned long)gpr_stats_inc);
+    ;
+    printf("%lx", (unsigned long)gpr_stats_read);
+    ;
+    printf("%lx", (unsigned long)gpr_thd_new);
+    ;
+    printf("%lx", (unsigned long)gpr_thd_options_default);
+    ;
+    printf("%lx", (unsigned long)gpr_thd_options_set_detached);
+    ;
+    printf("%lx", (unsigned long)gpr_thd_options_set_joinable);
+    ;
+    printf("%lx", (unsigned long)gpr_thd_options_is_detached);
+    ;
+    printf("%lx", (unsigned long)gpr_thd_options_is_joinable);
+    ;
+    printf("%lx", (unsigned long)gpr_thd_currentid);
+    ;
+    printf("%lx", (unsigned long)gpr_thd_join);
+    ;
+    printf("%lx", (unsigned long)gpr_time_0);
+    ;
+    printf("%lx", (unsigned long)gpr_inf_future);
+    ;
+    printf("%lx", (unsigned long)gpr_inf_past);
+    ;
+    printf("%lx", (unsigned long)gpr_time_init);
+    ;
+    printf("%lx", (unsigned long)gpr_now);
+    ;
+    printf("%lx", (unsigned long)gpr_convert_clock_type);
+    ;
+    printf("%lx", (unsigned long)gpr_time_cmp);
+    ;
+    printf("%lx", (unsigned long)gpr_time_max);
+    ;
+    printf("%lx", (unsigned long)gpr_time_min);
+    ;
+    printf("%lx", (unsigned long)gpr_time_add);
+    ;
+    printf("%lx", (unsigned long)gpr_time_sub);
+    ;
+    printf("%lx", (unsigned long)gpr_time_from_micros);
+    ;
+    printf("%lx", (unsigned long)gpr_time_from_nanos);
+    ;
+    printf("%lx", (unsigned long)gpr_time_from_millis);
+    ;
+    printf("%lx", (unsigned long)gpr_time_from_seconds);
+    ;
+    printf("%lx", (unsigned long)gpr_time_from_minutes);
+    ;
+    printf("%lx", (unsigned long)gpr_time_from_hours);
+    ;
+    printf("%lx", (unsigned long)gpr_time_to_millis);
+    ;
+    printf("%lx", (unsigned long)gpr_time_similar);
+    ;
+    printf("%lx", (unsigned long)gpr_sleep_until);
+    ;
+    printf("%lx", (unsigned long)gpr_timespec_to_micros);
+    ;
+  }
+  return 0;
+}
-- 
cgit v1.2.3

