From dddbf69d8f07d5ab7a4ddf7d2fdf5e91c031cb61 Mon Sep 17 00:00:00 2001
From: Craig Tiller <craig.tiller@gmail.com>
Date: Thu, 29 Jan 2015 10:25:33 -0800
Subject: [PATCH] Fix refcount leak on server rpc_new

---
 src/core/surface/call.c             | 14 +++++++++-----
 src/core/surface/call.h             |  4 ++--
 src/core/surface/completion_queue.c | 20 ++++++++++++++++++--
 src/core/surface/server.c           |  3 +++
 4 files changed, 32 insertions(+), 9 deletions(-)

diff --git a/src/core/surface/call.c b/src/core/surface/call.c
index 7e1148f6b0..87bba3ab0c 100644
--- a/src/core/surface/call.c
+++ b/src/core/surface/call.c
@@ -168,9 +168,13 @@ legacy_state *get_legacy_state(grpc_call *call) {
   return call->legacy_state;
 }
 
-void grpc_call_internal_ref(grpc_call *c) { gpr_ref(&c->internal_refcount); }
+void grpc_call_internal_ref(grpc_call *c, const char *reason) { 
+  gpr_log(GPR_DEBUG, "ref %p %s", c, reason);
+  gpr_ref(&c->internal_refcount); 
+}
 
-void grpc_call_internal_unref(grpc_call *c) {
+void grpc_call_internal_unref(grpc_call *c, const char *reason) {
+  gpr_log(GPR_DEBUG, "unref %p %s", c, reason);
   if (gpr_unref(&c->internal_refcount)) {
     grpc_call_stack_destroy(CALL_STACK_FROM_CALL(c));
     grpc_channel_internal_unref(c->channel);
@@ -560,7 +564,7 @@ void grpc_call_destroy(grpc_call *c) {
   cancel = !c->stream_closed;
   unlock(c);
   if (cancel) grpc_call_cancel(c);
-  grpc_call_internal_unref(c);
+  grpc_call_internal_unref(c, "destroy");
 }
 
 static void maybe_set_status_code(grpc_call *call, gpr_uint32 status) {
@@ -891,7 +895,7 @@ static void call_alarm(void *arg, int success) {
       grpc_call_cancel(call);
     }
   }
-  grpc_call_internal_unref(call);
+  grpc_call_internal_unref(call, "alarm");
 }
 
 void grpc_call_set_deadline(grpc_call_element *elem, gpr_timespec deadline) {
@@ -900,7 +904,7 @@ void grpc_call_set_deadline(grpc_call_element *elem, gpr_timespec deadline) {
   if (call->have_alarm) {
     gpr_log(GPR_ERROR, "Attempt to set deadline alarm twice");
   }
-  grpc_call_internal_ref(call);
+  grpc_call_internal_ref(call, "alarm");
   call->have_alarm = 1;
   grpc_alarm_init(&call->alarm, deadline, call_alarm, call, gpr_now());
 }
diff --git a/src/core/surface/call.h b/src/core/surface/call.h
index 10f8dbe6c8..6ed6fdf54a 100644
--- a/src/core/surface/call.h
+++ b/src/core/surface/call.h
@@ -45,8 +45,8 @@ typedef void (*grpc_ioreq_completion_func)(grpc_call *call,
 grpc_call *grpc_call_create(grpc_channel *channel,
                             const void *server_transport_data);
 
-void grpc_call_internal_ref(grpc_call *call);
-void grpc_call_internal_unref(grpc_call *call);
+void grpc_call_internal_ref(grpc_call *call, const char *reason);
+void grpc_call_internal_unref(grpc_call *call, const char *reason);
 
 /* Helpers for grpc_client, grpc_server filters to publish received data to
    the completion queue/surface layer */
diff --git a/src/core/surface/completion_queue.c b/src/core/surface/completion_queue.c
index 5854afbeef..6200864820 100644
--- a/src/core/surface/completion_queue.c
+++ b/src/core/surface/completion_queue.c
@@ -132,10 +132,26 @@ static event *add_locked(grpc_completion_queue *cc, grpc_completion_type type,
   return ev;
 }
 
+static char *op_string(grpc_completion_type type) {
+  switch (type) {
+    case GRPC_QUEUE_SHUTDOWN: return "shutdown";
+    case GRPC_IOREQ: return "ioreq";
+    case GRPC_WRITE_ACCEPTED: return "write_accepted";
+    case GRPC_READ: return "read";
+    case GRPC_FINISH_ACCEPTED: return "finish_accepted";
+    case GRPC_CLIENT_METADATA_READ: return "client_metadata_read";
+    case GRPC_FINISHED: return "finished";
+    case GRPC_SERVER_RPC_NEW: return "rpc_new";
+    case GRPC_SERVER_SHUTDOWN: return "server_shutdown";
+    case GRPC_COMPLETION_DO_NOT_USE: return "do_not_use";
+  }
+  return "unknown";
+}
+
 void grpc_cq_begin_op(grpc_completion_queue *cc, grpc_call *call,
                       grpc_completion_type type) {
   gpr_ref(&cc->refs);
-  if (call) grpc_call_internal_ref(call);
+  if (call) grpc_call_internal_ref(call, op_string(type));
 #ifndef NDEBUG
   gpr_atm_no_barrier_fetch_add(&cc->pending_op_count[type], 1);
 #endif
@@ -388,7 +404,7 @@ void grpc_event_finish(grpc_event *base) {
   event *ev = (event *)base;
   ev->on_finish(ev->on_finish_user_data, GRPC_OP_OK);
   if (ev->base.call) {
-    grpc_call_internal_unref(ev->base.call);
+    grpc_call_internal_unref(ev->base.call, op_string(base->type));
   }
   gpr_free(ev);
 }
diff --git a/src/core/surface/server.c b/src/core/surface/server.c
index fe03369ccc..8d31870e27 100644
--- a/src/core/surface/server.c
+++ b/src/core/surface/server.c
@@ -712,11 +712,14 @@ static void publish_legacy_request(grpc_call *call, grpc_op_error status,
   grpc_server *server = chand->server;
 
   if (status == GRPC_OP_OK) {
+    grpc_call_internal_ref(call, "rpc_new");
     grpc_cq_end_new_rpc(server->cq, tag, call, do_nothing, NULL,
                         grpc_mdstr_as_c_string(calld->path),
                         grpc_mdstr_as_c_string(calld->host), calld->deadline,
                         calld->legacy->client_metadata.count,
                         calld->legacy->client_metadata.metadata);
+  } else {
+    abort();
   }
 }
 
-- 
GitLab