{"action":"created","comment":{"url":"https://api.github.com/repos/grpc/grpc/pulls/comments/33725562","id":33725562,"diff_hunk":"@@ -183,192 +138,373 @@ static void handle_op_after_cancellation(grpc_call_element *elem,\n   }\n }\n \n-static void cc_start_transport_op(grpc_call_element *elem,\n-                                  grpc_transport_op *op) {\n+typedef struct {\n+  grpc_iomgr_closure closure;\n+  grpc_call_element *elem;\n+} waiting_call;\n+\n+static void perform_transport_stream_op(grpc_call_element *elem,\n+                                        grpc_transport_stream_op *op,\n+                                        int continuation);\n+\n+static void continue_with_pick(void *arg, int iomgr_success) {\n+  waiting_call *wc = arg;\n+  call_data *calld = wc->elem->call_data;\n+  perform_transport_stream_op(wc->elem, &calld->waiting_op, 1);\n+  gpr_free(wc);\n+}\n+\n+static void add_to_lb_policy_wait_queue_locked_state_config(\n+    grpc_call_element *elem) {\n+  channel_data *chand = elem->channel_data;\n+  waiting_call *wc = gpr_malloc(sizeof(*wc));\n+  grpc_iomgr_closure_init(&wc->closure, continue_with_pick, wc);\n+  wc->elem = elem;\n+  wc->closure.next = chand->waiting_for_config_closures;\n+  chand->waiting_for_config_closures = &wc->closure;\n+}\n+\n+static int is_empty(void *p, int len) {\n+  char *ptr = p;\n+  int i;\n+  for (i = 0; i < len; i++) {\n+    if (ptr[i] != 0) return 0;\n+  }\n+  return 1;\n+}\n+\n+static void started_call(void *arg, int iomgr_success) {\n+  call_data *calld = arg;\n+  grpc_transport_stream_op op;\n+  int have_waiting;\n+\n+  gpr_mu_lock(&calld->mu_state);\n+  if (calld->state == CALL_CANCELLED && calld->subchannel_call != NULL) {\n+    memset(&op, 0, sizeof(op));\n+    op.cancel_with_status = GRPC_STATUS_CANCELLED;\n+    gpr_mu_unlock(&calld->mu_state);\n+    grpc_subchannel_call_process_op(calld->subchannel_call, &op);\n+  } else if (calld->state == CALL_WAITING_FOR_CALL) {\n+    have_waiting = !is_empty(&calld->waiting_op, sizeof(calld->waiting_op));\n+    if (calld->subchannel_call != NULL) {\n+      calld->state = CALL_ACTIVE;\n+      gpr_mu_unlock(&calld->mu_state);\n+      if (have_waiting) {\n+        grpc_subchannel_call_process_op(calld->subchannel_call,\n+                                        &calld->waiting_op);\n+      }\n+    } else {\n+      calld->state = CALL_CANCELLED;\n+      gpr_mu_unlock(&calld->mu_state);\n+      if (have_waiting) {\n+        handle_op_after_cancellation(calld->elem, &calld->waiting_op);\n+      }\n+    }\n+  } else {\n+    GPR_ASSERT(calld->state == CALL_CANCELLED);\n+  }\n+}\n+\n+static void picked_target(void *arg, int iomgr_success) {\n+  call_data *calld = arg;\n+  grpc_pollset *pollset;\n+\n+  if (calld->picked_channel == NULL) {\n+    /* treat this like a cancellation */\n+    calld->waiting_op.cancel_with_status = GRPC_STATUS_UNAVAILABLE;\n+    perform_transport_stream_op(calld->elem, &calld->waiting_op, 1);\n+  } else {\n+    gpr_mu_lock(&calld->mu_state);\n+    if (calld->state == CALL_CANCELLED) {\n+      gpr_mu_unlock(&calld->mu_state);\n+      handle_op_after_cancellation(calld->elem, &calld->waiting_op);\n+    } else {\n+      GPR_ASSERT(calld->state == CALL_WAITING_FOR_PICK);\n+      calld->state = CALL_WAITING_FOR_CALL;\n+      pollset = calld->waiting_op.bind_pollset;\n+      gpr_mu_unlock(&calld->mu_state);\n+      grpc_iomgr_closure_init(&calld->async_setup_task, started_call, calld);\n+      grpc_subchannel_create_call(calld->picked_channel, pollset,\n+                                  &calld->subchannel_call,\n+                                  &calld->async_setup_task);\n+    }\n+  }\n+}\n+\n+static void pick_target(grpc_lb_policy *lb_policy, call_data *calld) {\n+  grpc_metadata_batch *initial_metadata;\n+  grpc_transport_stream_op *op = &calld->waiting_op;\n+\n+  GPR_ASSERT(op->bind_pollset);\n+  GPR_ASSERT(op->send_ops);\n+  GPR_ASSERT(op->send_ops->nops >= 1);\n+  GPR_ASSERT(op->send_ops->ops[0].type == GRPC_OP_METADATA);\n+  initial_metadata = &op->send_ops->ops[0].data.metadata;\n+\n+  grpc_iomgr_closure_init(&calld->async_setup_task, picked_target, calld);\n+  grpc_lb_policy_pick(lb_policy, op->bind_pollset, initial_metadata,\n+                      &calld->picked_channel, &calld->async_setup_task);\n+}\n+\n+static grpc_iomgr_closure *merge_into_waiting_op(\n+    grpc_call_element *elem, grpc_transport_stream_op *new_op) {\n+  call_data *calld = elem->call_data;\n+  grpc_iomgr_closure *consumed_op = NULL;\n+  grpc_transport_stream_op *waiting_op = &calld->waiting_op;\n+  GPR_ASSERT((waiting_op->send_ops == NULL) != (new_op->send_ops == NULL) ||\n+             waiting_op->send_ops == NULL);\n+  GPR_ASSERT((waiting_op->recv_ops == NULL) != (new_op->recv_ops == NULL) ||\n+             waiting_op->recv_ops == NULL);\n+  if (new_op->send_ops != NULL) {\n+    waiting_op->send_ops = new_op->send_ops;\n+    waiting_op->is_last_send = new_op->is_last_send;\n+    waiting_op->on_done_send = new_op->on_done_send;\n+  }\n+  if (new_op->recv_ops != NULL) {\n+    waiting_op->recv_ops = new_op->recv_ops;\n+    waiting_op->recv_state = new_op->recv_state;\n+    waiting_op->on_done_recv = new_op->on_done_recv;\n+  }\n+  if (new_op->on_consumed != NULL) {\n+    if (waiting_op->on_consumed != NULL) {\n+      consumed_op = waiting_op->on_consumed;\n+    }\n+    waiting_op->on_consumed = new_op->on_consumed;\n+  }\n+  if (new_op->cancel_with_status != GRPC_STATUS_OK) {\n+    waiting_op->cancel_with_status = new_op->cancel_with_status;\n+  }\n+  return consumed_op;\n+}\n+\n+static void perform_transport_stream_op(grpc_call_element *elem,\n+                                        grpc_transport_stream_op *op,\n+                                        int continuation) {\n   call_data *calld = elem->call_data;\n   channel_data *chand = elem->channel_data;\n-  grpc_call_element *child_elem;\n-  grpc_transport_op waiting_op;\n+  grpc_subchannel_call *subchannel_call;\n+  grpc_lb_policy *lb_policy;\n+  grpc_transport_stream_op op2;\n+  grpc_iomgr_closure *consumed_op = NULL;\n   GPR_ASSERT(elem->filter == &grpc_client_channel_filter);\n   GRPC_CALL_LOG_OP(GPR_INFO, elem, op);\n \n-  gpr_mu_lock(&chand->mu);\n+  gpr_mu_lock(&calld->mu_state);\n   switch (calld->state) {\n     case CALL_ACTIVE:\n-      child_elem = grpc_child_call_get_top_element(calld->s.active.child_call);\n-      gpr_mu_unlock(&chand->mu);\n-      child_elem->filter->start_transport_op(child_elem, op);\n+      GPR_ASSERT(!continuation);\n+      subchannel_call = calld->subchannel_call;\n+      gpr_mu_unlock(&calld->mu_state);\n+      grpc_subchannel_call_process_op(subchannel_call, op);\n       break;\n-    case CALL_CREATED:\n-      if (op->cancel_with_status != GRPC_STATUS_OK) {\n-        calld->state = CALL_CANCELLED;\n-        gpr_mu_unlock(&chand->mu);\n-        handle_op_after_cancellation(elem, op);\n-      } else {\n-        calld->state = CALL_WAITING;\n-        calld->s.waiting_op.bind_pollset = NULL;\n-        if (chand->active_child) {\n-          /* channel is connected - use the connected stack */\n-          if (prepare_activate(elem, chand->active_child)) {\n-            gpr_mu_unlock(&chand->mu);\n-            /* activate the request (pass it down) outside the lock */\n-            complete_activate(elem, op);\n-          } else {\n-            gpr_mu_unlock(&chand->mu);\n+    case CALL_CANCELLED:\n+      gpr_mu_unlock(&calld->mu_state);\n+      handle_op_after_cancellation(elem, op);\n+      break;\n+    case CALL_WAITING_FOR_SEND:\n+      GPR_ASSERT(!continuation);\n+      consumed_op = merge_into_waiting_op(elem, op);\n+      if (!calld->waiting_op.send_ops &&\n+          calld->waiting_op.cancel_with_status == GRPC_STATUS_OK) {\n+        gpr_mu_unlock(&calld->mu_state);\n+        break;\n+      }\n+      *op = calld->waiting_op;\n+      memset(&calld->waiting_op, 0, sizeof(calld->waiting_op));\n+      continuation = 1;\n+    /* fall through */\n+    case CALL_WAITING_FOR_CONFIG:\n+    case CALL_WAITING_FOR_PICK:\n+    case CALL_WAITING_FOR_CALL:\n+      if (!continuation) {\n+        if (op->cancel_with_status != GRPC_STATUS_OK) {\n+          calld->state = CALL_CANCELLED;\n+          op2 = calld->waiting_op;\n+          memset(&calld->waiting_op, 0, sizeof(calld->waiting_op));\n+          if (op->on_consumed) {\n+            calld->waiting_op.on_consumed = op->on_consumed;\n+            op->on_consumed = NULL;\n+          } else if (op2.on_consumed) {\n+            calld->waiting_op.on_consumed = op2.on_consumed;\n+            op2.on_consumed = NULL;\n           }\n+          gpr_mu_unlock(&calld->mu_state);\n+          handle_op_after_cancellation(elem, op);\n+          handle_op_after_cancellation(elem, &op2);\n         } else {\n-          /* check to see if we should initiate a connection (if we're not\n-             already),\n-             but don't do so until outside the lock to avoid re-entrancy\n-             problems if\n-             the callback is immediate */\n-          int initiate_transport_setup = 0;\n-          if (!chand->transport_setup_initiated) {\n-            chand->transport_setup_initiated = 1;\n-            initiate_transport_setup = 1;\n-          }\n-          /* add this call to the waiting set to be resumed once we have a child\n-             channel stack, growing the waiting set if needed */\n-          if (chand->waiting_child_count == chand->waiting_child_capacity) {\n-            chand->waiting_child_capacity =\n-                GPR_MAX(chand->waiting_child_capacity * 2, 8);\n-            chand->waiting_children = gpr_realloc(\n-                chand->waiting_children,\n-                chand->waiting_child_capacity * sizeof(call_data *));\n-          }\n-          calld->s.waiting_op = *op;\n-          chand->waiting_children[chand->waiting_child_count++] = calld;\n-          grpc_transport_setup_add_interested_party(chand->transport_setup,\n-                                                    op->bind_pollset);\n-          gpr_mu_unlock(&chand->mu);\n-\n-          /* finally initiate transport setup if needed */\n-          if (initiate_transport_setup) {\n-            grpc_transport_setup_initiate(chand->transport_setup);\n-          }\n+          consumed_op = merge_into_waiting_op(elem, op);\n+          gpr_mu_unlock(&calld->mu_state);\n         }\n+        break;\n       }\n-      break;\n-    case CALL_WAITING:\n+    /* fall through */\n+    case CALL_CREATED:\n       if (op->cancel_with_status != GRPC_STATUS_OK) {\n-        waiting_op = calld->s.waiting_op;\n-        remove_waiting_child(chand, calld);\n         calld->state = CALL_CANCELLED;\n-        gpr_mu_unlock(&chand->mu);\n-        handle_op_after_cancellation(elem, &waiting_op);\n+        gpr_mu_unlock(&calld->mu_state);\n         handle_op_after_cancellation(elem, op);\n       } else {\n-        GPR_ASSERT((calld->s.waiting_op.send_ops == NULL) !=\n-                   (op->send_ops == NULL));\n-        GPR_ASSERT((calld->s.waiting_op.recv_ops == NULL) !=\n-                   (op->recv_ops == NULL));\n-        if (op->send_ops) {\n-          calld->s.waiting_op.send_ops = op->send_ops;\n-          calld->s.waiting_op.is_last_send = op->is_last_send;\n-          calld->s.waiting_op.on_done_send = op->on_done_send;\n-        }\n-        if (op->recv_ops) {\n-          calld->s.waiting_op.recv_ops = op->recv_ops;\n-          calld->s.waiting_op.recv_state = op->recv_state;\n-          calld->s.waiting_op.on_done_recv = op->on_done_recv;\n-        }\n-        gpr_mu_unlock(&chand->mu);\n-        if (op->on_consumed) {\n-          op->on_consumed->cb(op->on_consumed->cb_arg, 0);\n+        calld->waiting_op = *op;\n+\n+        if (op->send_ops == NULL) {\n+          /* need to have some send ops before we can select the\n+             lb target */\n+          calld->state = CALL_WAITING_FOR_SEND;\n+          gpr_mu_unlock(&calld->mu_state);\n+        } else {\n+          gpr_mu_lock(&chand->mu_config);\n+          lb_policy = chand->lb_policy;\n+          if (lb_policy) {\n+            GRPC_LB_POLICY_REF(lb_policy, \"pick\");\n+            gpr_mu_unlock(&chand->mu_config);\n+            calld->state = CALL_WAITING_FOR_PICK;\n+            gpr_mu_unlock(&calld->mu_state);\n+\n+            pick_target(lb_policy, calld);\n+\n+            GRPC_LB_POLICY_UNREF(lb_policy, \"pick\");\n+          } else if (chand->resolver != NULL) {\n+            calld->state = CALL_WAITING_FOR_CONFIG;\n+            add_to_lb_policy_wait_queue_locked_state_config(elem);\n+            gpr_mu_unlock(&chand->mu_config);\n+            gpr_mu_unlock(&calld->mu_state);\n+          } else {\n+            calld->state = CALL_CANCELLED;\n+            gpr_mu_unlock(&chand->mu_config);\n+            gpr_mu_unlock(&calld->mu_state);\n+            handle_op_after_cancellation(elem, op);\n+          }\n         }\n       }\n       break;\n-    case CALL_CANCELLED:\n-      gpr_mu_unlock(&chand->mu);\n-      handle_op_after_cancellation(elem, op);\n-      break;\n   }\n+\n+  if (consumed_op != NULL) {\n+    consumed_op->cb(consumed_op->cb_arg, 1);\n+  }\n+}\n+\n+static void cc_start_transport_stream_op(grpc_call_element *elem,\n+                                         grpc_transport_stream_op *op) {\n+  perform_transport_stream_op(elem, op, 0);\n+}\n+\n+static void cc_on_config_changed(void *arg, int iomgr_success) {\n+  channel_data *chand = arg;\n+  grpc_lb_policy *lb_policy = NULL;\n+  grpc_lb_policy *old_lb_policy;\n+  grpc_resolver *old_resolver;\n+  grpc_iomgr_closure *wakeup_closures = NULL;\n+\n+  if (chand->incoming_configuration != NULL) {\n+    lb_policy = grpc_client_config_get_lb_policy(chand->incoming_configuration);\n+    GRPC_LB_POLICY_REF(lb_policy, \"channel\");\n+\n+    grpc_client_config_unref(chand->incoming_configuration);\n+  }\n+\n+  chand->incoming_configuration = NULL;\n+\n+  gpr_mu_lock(&chand->mu_config);\n+  old_lb_policy = chand->lb_policy;\n+  chand->lb_policy = lb_policy;\n+  if (lb_policy != NULL || chand->resolver == NULL /* disconnected */) {\n+    wakeup_closures = chand->waiting_for_config_closures;\n+    chand->waiting_for_config_closures = NULL;\n+  }\n+  gpr_mu_unlock(&chand->mu_config);\n+\n+  if (old_lb_policy) {\n+    GRPC_LB_POLICY_UNREF(old_lb_policy, \"channel\");\n+  }\n+\n+  gpr_mu_lock(&chand->mu_config);","path":"src/core/channel/client_channel.c","position":547,"original_position":548,"commit_id":"684d8f600d751c226ea70b0db92bc7d50c595bbf","original_commit_id":"b3671531159b7bbce54c4c3ee4895380a4c968fc","user":{"login":"yang-g","id":10122250,"avatar_url":"https://avatars.githubusercontent.com/u/10122250?v=3","gravatar_id":"","url":"https://api.github.com/users/yang-g","html_url":"https://github.com/yang-g","followers_url":"https://api.github.com/users/yang-g/followers","following_url":"https://api.github.com/users/yang-g/following{/other_user}","gists_url":"https://api.github.com/users/yang-g/gists{/gist_id}","starred_url":"https://api.github.com/users/yang-g/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/yang-g/subscriptions","organizations_url":"https://api.github.com/users/yang-g/orgs","repos_url":"https://api.github.com/users/yang-g/repos","events_url":"https://api.github.com/users/yang-g/events{/privacy}","received_events_url":"https://api.github.com/users/yang-g/received_events","type":"User","site_admin":false},"body":"what is this lock protecting?","created_at":"2015-07-01T21:16:17Z","updated_at":"2015-07-01T21:16:17Z","html_url":"https://github.com/grpc/grpc/pull/2208#discussion_r33725562","pull_request_url":"https://api.github.com/repos/grpc/grpc/pulls/2208","_links":{"self":{"href":"https://api.github.com/repos/grpc/grpc/pulls/comments/33725562"},"html":{"href":"https://github.com/grpc/grpc/pull/2208#discussion_r33725562"},"pull_request":{"href":"https://api.github.com/repos/grpc/grpc/pulls/2208"}}},"pull_request":{"url":"https://api.github.com/repos/grpc/grpc/pulls/2208","id":38497313,"html_url":"https://github.com/grpc/grpc/pull/2208","diff_url":"https://github.com/grpc/grpc/pull/2208.diff","patch_url":"https://github.com/grpc/grpc/pull/2208.patch","issue_url":"https://api.github.com/repos/grpc/grpc/issues/2208","number":2208,"state":"open","locked":false,"title":"Client configuration for core","user":{"login":"ctiller","id":10120821,"avatar_url":"https://avatars.githubusercontent.com/u/10120821?v=3","gravatar_id":"","url":"https://api.github.com/users/ctiller","html_url":"https://github.com/ctiller","followers_url":"https://api.github.com/users/ctiller/followers","following_url":"https://api.github.com/users/ctiller/following{/other_user}","gists_url":"https://api.github.com/users/ctiller/gists{/gist_id}","starred_url":"https://api.github.com/users/ctiller/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ctiller/subscriptions","organizations_url":"https://api.github.com/users/ctiller/orgs","repos_url":"https://api.github.com/users/ctiller/repos","events_url":"https://api.github.com/users/ctiller/events{/privacy}","received_events_url":"https://api.github.com/users/ctiller/received_events","type":"User","site_admin":false},"body":"","created_at":"2015-06-24T23:06:41Z","updated_at":"2015-07-01T21:16:17Z","closed_at":null,"merged_at":null,"merge_commit_sha":"2de239c7e4747eced934c98419d08ebe2582a395","assignee":{"login":"yang-g","id":10122250,"avatar_url":"https://avatars.githubusercontent.com/u/10122250?v=3","gravatar_id":"","url":"https://api.github.com/users/yang-g","html_url":"https://github.com/yang-g","followers_url":"https://api.github.com/users/yang-g/followers","following_url":"https://api.github.com/users/yang-g/following{/other_user}","gists_url":"https://api.github.com/users/yang-g/gists{/gist_id}","starred_url":"https://api.github.com/users/yang-g/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/yang-g/subscriptions","organizations_url":"https://api.github.com/users/yang-g/orgs","repos_url":"https://api.github.com/users/yang-g/repos","events_url":"https://api.github.com/users/yang-g/events{/privacy}","received_events_url":"https://api.github.com/users/yang-g/received_events","type":"User","site_admin":false},"milestone":null,"commits_url":"https://api.github.com/repos/grpc/grpc/pulls/2208/commits","review_comments_url":"https://api.github.com/repos/grpc/grpc/pulls/2208/comments","review_comment_url":"https://api.github.com/repos/grpc/grpc/pulls/comments{/number}","comments_url":"https://api.github.com/repos/grpc/grpc/issues/2208/comments","statuses_url":"https://api.github.com/repos/grpc/grpc/statuses/684d8f600d751c226ea70b0db92bc7d50c595bbf","head":{"label":"ctiller:tis-but-thy-name","ref":"tis-but-thy-name","sha":"684d8f600d751c226ea70b0db92bc7d50c595bbf","user":{"login":"ctiller","id":10120821,"avatar_url":"https://avatars.githubusercontent.com/u/10120821?v=3","gravatar_id":"","url":"https://api.github.com/users/ctiller","html_url":"https://github.com/ctiller","followers_url":"https://api.github.com/users/ctiller/followers","following_url":"https://api.github.com/users/ctiller/following{/other_user}","gists_url":"https://api.github.com/users/ctiller/gists{/gist_id}","starred_url":"https://api.github.com/users/ctiller/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ctiller/subscriptions","organizations_url":"https://api.github.com/users/ctiller/orgs","repos_url":"https://api.github.com/users/ctiller/repos","events_url":"https://api.github.com/users/ctiller/events{/privacy}","received_events_url":"https://api.github.com/users/ctiller/received_events","type":"User","site_admin":false},"repo":{"id":31379631,"name":"grpc","full_name":"ctiller/grpc","owner":{"login":"ctiller","id":10120821,"avatar_url":"https://avatars.githubusercontent.com/u/10120821?v=3","gravatar_id":"","url":"https://api.github.com/users/ctiller","html_url":"https://github.com/ctiller","followers_url":"https://api.github.com/users/ctiller/followers","following_url":"https://api.github.com/users/ctiller/following{/other_user}","gists_url":"https://api.github.com/users/ctiller/gists{/gist_id}","starred_url":"https://api.github.com/users/ctiller/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ctiller/subscriptions","organizations_url":"https://api.github.com/users/ctiller/orgs","repos_url":"https://api.github.com/users/ctiller/repos","events_url":"https://api.github.com/users/ctiller/events{/privacy}","received_events_url":"https://api.github.com/users/ctiller/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/ctiller/grpc","description":"The C based gRPC (C++, Node.js, Python, Ruby, Objective-C, PHP, C#)","fork":true,"url":"https://api.github.com/repos/ctiller/grpc","forks_url":"https://api.github.com/repos/ctiller/grpc/forks","keys_url":"https://api.github.com/repos/ctiller/grpc/keys{/key_id}","collaborators_url":"https://api.github.com/repos/ctiller/grpc/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/ctiller/grpc/teams","hooks_url":"https://api.github.com/repos/ctiller/grpc/hooks","issue_events_url":"https://api.github.com/repos/ctiller/grpc/issues/events{/number}","events_url":"https://api.github.com/repos/ctiller/grpc/events","assignees_url":"https://api.github.com/repos/ctiller/grpc/assignees{/user}","branches_url":"https://api.github.com/repos/ctiller/grpc/branches{/branch}","tags_url":"https://api.github.com/repos/ctiller/grpc/tags","blobs_url":"https://api.github.com/repos/ctiller/grpc/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/ctiller/grpc/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/ctiller/grpc/git/refs{/sha}","trees_url":"https://api.github.com/repos/ctiller/grpc/git/trees{/sha}","statuses_url":"https://api.github.com/repos/ctiller/grpc/statuses/{sha}","languages_url":"https://api.github.com/repos/ctiller/grpc/languages","stargazers_url":"https://api.github.com/repos/ctiller/grpc/stargazers","contributors_url":"https://api.github.com/repos/ctiller/grpc/contributors","subscribers_url":"https://api.github.com/repos/ctiller/grpc/subscribers","subscription_url":"https://api.github.com/repos/ctiller/grpc/subscription","commits_url":"https://api.github.com/repos/ctiller/grpc/commits{/sha}","git_commits_url":"https://api.github.com/repos/ctiller/grpc/git/commits{/sha}","comments_url":"https://api.github.com/repos/ctiller/grpc/comments{/number}","issue_comment_url":"https://api.github.com/repos/ctiller/grpc/issues/comments{/number}","contents_url":"https://api.github.com/repos/ctiller/grpc/contents/{+path}","compare_url":"https://api.github.com/repos/ctiller/grpc/compare/{base}...{head}","merges_url":"https://api.github.com/repos/ctiller/grpc/merges","archive_url":"https://api.github.com/repos/ctiller/grpc/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/ctiller/grpc/downloads","issues_url":"https://api.github.com/repos/ctiller/grpc/issues{/number}","pulls_url":"https://api.github.com/repos/ctiller/grpc/pulls{/number}","milestones_url":"https://api.github.com/repos/ctiller/grpc/milestones{/number}","notifications_url":"https://api.github.com/repos/ctiller/grpc/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/ctiller/grpc/labels{/name}","releases_url":"https://api.github.com/repos/ctiller/grpc/releases{/id}","created_at":"2015-02-26T17:36:22Z","updated_at":"2015-04-20T15:39:22Z","pushed_at":"2015-07-01T20:56:17Z","git_url":"git://github.com/ctiller/grpc.git","ssh_url":"git@github.com:ctiller/grpc.git","clone_url":"https://github.com/ctiller/grpc.git","svn_url":"https://github.com/ctiller/grpc","homepage":"","size":43082,"stargazers_count":2,"watchers_count":2,"language":"C","has_issues":false,"has_downloads":false,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"open_issues_count":0,"forks":0,"open_issues":0,"watchers":2,"default_branch":"master"}},"base":{"label":"grpc:master","ref":"master","sha":"f8c63562c1388262c5588e381c05cb0c02e5b406","user":{"login":"grpc","id":7802525,"avatar_url":"https://avatars.githubusercontent.com/u/7802525?v=3","gravatar_id":"","url":"https://api.github.com/users/grpc","html_url":"https://github.com/grpc","followers_url":"https://api.github.com/users/grpc/followers","following_url":"https://api.github.com/users/grpc/following{/other_user}","gists_url":"https://api.github.com/users/grpc/gists{/gist_id}","starred_url":"https://api.github.com/users/grpc/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/grpc/subscriptions","organizations_url":"https://api.github.com/users/grpc/orgs","repos_url":"https://api.github.com/users/grpc/repos","events_url":"https://api.github.com/users/grpc/events{/privacy}","received_events_url":"https://api.github.com/users/grpc/received_events","type":"Organization","site_admin":false},"repo":{"id":27729880,"name":"grpc","full_name":"grpc/grpc","owner":{"login":"grpc","id":7802525,"avatar_url":"https://avatars.githubusercontent.com/u/7802525?v=3","gravatar_id":"","url":"https://api.github.com/users/grpc","html_url":"https://github.com/grpc","followers_url":"https://api.github.com/users/grpc/followers","following_url":"https://api.github.com/users/grpc/following{/other_user}","gists_url":"https://api.github.com/users/grpc/gists{/gist_id}","starred_url":"https://api.github.com/users/grpc/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/grpc/subscriptions","organizations_url":"https://api.github.com/users/grpc/orgs","repos_url":"https://api.github.com/users/grpc/repos","events_url":"https://api.github.com/users/grpc/events{/privacy}","received_events_url":"https://api.github.com/users/grpc/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/grpc/grpc","description":"The C based gRPC (C++, Node.js, Python, Ruby, Objective-C, PHP, C#)","fork":false,"url":"https://api.github.com/repos/grpc/grpc","forks_url":"https://api.github.com/repos/grpc/grpc/forks","keys_url":"https://api.github.com/repos/grpc/grpc/keys{/key_id}","collaborators_url":"https://api.github.com/repos/grpc/grpc/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/grpc/grpc/teams","hooks_url":"https://api.github.com/repos/grpc/grpc/hooks","issue_events_url":"https://api.github.com/repos/grpc/grpc/issues/events{/number}","events_url":"https://api.github.com/repos/grpc/grpc/events","assignees_url":"https://api.github.com/repos/grpc/grpc/assignees{/user}","branches_url":"https://api.github.com/repos/grpc/grpc/branches{/branch}","tags_url":"https://api.github.com/repos/grpc/grpc/tags","blobs_url":"https://api.github.com/repos/grpc/grpc/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/grpc/grpc/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/grpc/grpc/git/refs{/sha}","trees_url":"https://api.github.com/repos/grpc/grpc/git/trees{/sha}","statuses_url":"https://api.github.com/repos/grpc/grpc/statuses/{sha}","languages_url":"https://api.github.com/repos/grpc/grpc/languages","stargazers_url":"https://api.github.com/repos/grpc/grpc/stargazers","contributors_url":"https://api.github.com/repos/grpc/grpc/contributors","subscribers_url":"https://api.github.com/repos/grpc/grpc/subscribers","subscription_url":"https://api.github.com/repos/grpc/grpc/subscription","commits_url":"https://api.github.com/repos/grpc/grpc/commits{/sha}","git_commits_url":"https://api.github.com/repos/grpc/grpc/git/commits{/sha}","comments_url":"https://api.github.com/repos/grpc/grpc/comments{/number}","issue_comment_url":"https://api.github.com/repos/grpc/grpc/issues/comments{/number}","contents_url":"https://api.github.com/repos/grpc/grpc/contents/{+path}","compare_url":"https://api.github.com/repos/grpc/grpc/compare/{base}...{head}","merges_url":"https://api.github.com/repos/grpc/grpc/merges","archive_url":"https://api.github.com/repos/grpc/grpc/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/grpc/grpc/downloads","issues_url":"https://api.github.com/repos/grpc/grpc/issues{/number}","pulls_url":"https://api.github.com/repos/grpc/grpc/pulls{/number}","milestones_url":"https://api.github.com/repos/grpc/grpc/milestones{/number}","notifications_url":"https://api.github.com/repos/grpc/grpc/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/grpc/grpc/labels{/name}","releases_url":"https://api.github.com/repos/grpc/grpc/releases{/id}","created_at":"2014-12-08T18:58:53Z","updated_at":"2015-07-01T20:32:52Z","pushed_at":"2015-07-01T21:00:23Z","git_url":"git://github.com/grpc/grpc.git","ssh_url":"git@github.com:grpc/grpc.git","clone_url":"https://github.com/grpc/grpc.git","svn_url":"https://github.com/grpc/grpc","homepage":"","size":61126,"stargazers_count":2800,"watchers_count":2800,"language":"C","has_issues":true,"has_downloads":false,"has_wiki":true,"has_pages":false,"forks_count":401,"mirror_url":null,"open_issues_count":352,"forks":401,"open_issues":352,"watchers":2800,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/grpc/grpc/pulls/2208"},"html":{"href":"https://github.com/grpc/grpc/pull/2208"},"issue":{"href":"https://api.github.com/repos/grpc/grpc/issues/2208"},"comments":{"href":"https://api.github.com/repos/grpc/grpc/issues/2208/comments"},"review_comments":{"href":"https://api.github.com/repos/grpc/grpc/pulls/2208/comments"},"review_comment":{"href":"https://api.github.com/repos/grpc/grpc/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/grpc/grpc/pulls/2208/commits"},"statuses":{"href":"https://api.github.com/repos/grpc/grpc/statuses/684d8f600d751c226ea70b0db92bc7d50c595bbf"}}}}