{"action":"created","comment":{"url":"https://api.github.com/repos/grpc/grpc-java/pulls/comments/128297217","pull_request_review_id":50779105,"id":128297217,"diff_hunk":"@@ -16,66 +16,97 @@\n \n package io.grpc.internal;\n \n+import static com.google.common.base.Preconditions.checkNotNull;\n+import static com.google.common.base.Preconditions.checkState;\n+\n import io.grpc.ConnectivityState;\n+import io.grpc.ManagedChannel;\n import java.util.ArrayList;\n import java.util.concurrent.Executor;\n+import javax.annotation.Nonnull;\n+import javax.annotation.Nullable;\n import javax.annotation.concurrent.NotThreadSafe;\n \n /**\n- * Book-keeps the connectivity state and callbacks.\n+ * Manages connectivity states of the channel. Used for {@link ManagedChannel#getState} to read the\n+ * current state of the channel, for {@link ManagedChannel#notifyWhenStateChanged} to add\n+ * listeners to state change events, and for {@link io.grpc.LoadBalancer.Helper#updateBalancingState\n+ * LoadBalancer.Helper#updateBalancingState} to update the state and run the {@link #gotoState}s.\n  */\n @NotThreadSafe\n-class ConnectivityStateManager {\n-  private ArrayList<StateCallbackEntry> callbacks;\n-\n-  private ConnectivityState state;\n+final class ConnectivityStateManager {\n+  private ArrayList<Listener> listeners = new ArrayList<Listener>();\n \n-  ConnectivityStateManager(ConnectivityState initialState) {\n-    state = initialState;\n-  }\n+  private volatile ConnectivityState state = ConnectivityState.IDLE;\n \n+  /**\n+   * Adds a listener for state change event.\n+   *\n+   * <p>The {@code executor} must be one that can run RPC call listeners.\n+   */\n   void notifyWhenStateChanged(Runnable callback, Executor executor, ConnectivityState source) {\n-    StateCallbackEntry callbackEntry = new StateCallbackEntry(callback, executor);\n+    checkNotNull(callback, \"callback\");\n+    checkNotNull(executor, \"executor\");\n+    checkNotNull(source, \"source\");\n+\n+    Listener stateChangeListener = new Listener(callback, executor);\n     if (state != source) {\n-      callbackEntry.runInExecutor();\n+      stateChangeListener.runInExecutor();\n     } else {\n-      if (callbacks == null) {\n-        callbacks = new ArrayList<StateCallbackEntry>();\n-      }\n-      callbacks.add(callbackEntry);\n+      listeners.add(stateChangeListener);\n     }\n   }\n \n-  // TODO(zhangkun83): return a runnable in order to escape transport set lock, in case direct\n-  // executor is used?\n-  void gotoState(ConnectivityState newState) {\n-    if (state != newState) {\n-      if (state == ConnectivityState.SHUTDOWN) {\n-        throw new IllegalStateException(\"Cannot transition out of SHUTDOWN to \" + newState);\n-      }\n+  /**\n+   * Connectivity state is changed to the specified value. Will trigger some notifications that have\n+   * been registered earlier by {@link ManagedChannel#notifyWhenStateChanged}.\n+   */\n+  void gotoState(@Nonnull ConnectivityState newState) {\n+    checkNotNull(newState, \"newState\");\n+    checkState(state != null, \"ConnectivityStateManager is already disabled\");\n+    gotoNullableState(newState);\n+  }\n+\n+  private void gotoNullableState(@Nullable ConnectivityState newState) {\n+    if (state != newState && state != ConnectivityState.SHUTDOWN) {","path":"core/src/main/java/io/grpc/internal/ConnectivityStateManager.java","position":77,"original_position":77,"commit_id":"da151c1df0be687032fb6348df2a3671b05e5459","original_commit_id":"da151c1df0be687032fb6348df2a3671b05e5459","user":{"login":"dapengzhang0","id":4779759,"avatar_url":"https://avatars2.githubusercontent.com/u/4779759?v=4","gravatar_id":"","url":"https://api.github.com/users/dapengzhang0","html_url":"https://github.com/dapengzhang0","followers_url":"https://api.github.com/users/dapengzhang0/followers","following_url":"https://api.github.com/users/dapengzhang0/following{/other_user}","gists_url":"https://api.github.com/users/dapengzhang0/gists{/gist_id}","starred_url":"https://api.github.com/users/dapengzhang0/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/dapengzhang0/subscriptions","organizations_url":"https://api.github.com/users/dapengzhang0/orgs","repos_url":"https://api.github.com/users/dapengzhang0/repos","events_url":"https://api.github.com/users/dapengzhang0/events{/privacy}","received_events_url":"https://api.github.com/users/dapengzhang0/received_events","type":"User","site_admin":false},"body":"Otherwise, the current `state == SHUTDOWN` and `newState != SHUTDOWN`. `SHUTDOWN` is the final state, we shouldn't change the state out of `SHUTDOWN`","created_at":"2017-07-19T16:25:03Z","updated_at":"2017-07-19T16:26:58Z","html_url":"https://github.com/grpc/grpc-java/pull/3019#discussion_r128297217","pull_request_url":"https://api.github.com/repos/grpc/grpc-java/pulls/3019","_links":{"self":{"href":"https://api.github.com/repos/grpc/grpc-java/pulls/comments/128297217"},"html":{"href":"https://github.com/grpc/grpc-java/pull/3019#discussion_r128297217"},"pull_request":{"href":"https://api.github.com/repos/grpc/grpc-java/pulls/3019"}}},"pull_request":{"url":"https://api.github.com/repos/grpc/grpc-java/pulls/3019","id":121379243,"html_url":"https://github.com/grpc/grpc-java/pull/3019","diff_url":"https://github.com/grpc/grpc-java/pull/3019.diff","patch_url":"https://github.com/grpc/grpc-java/pull/3019.patch","issue_url":"https://api.github.com/repos/grpc/grpc-java/issues/3019","number":3019,"state":"open","locked":false,"title":"POC: channel state API plumbing","user":{"login":"dapengzhang0","id":4779759,"avatar_url":"https://avatars2.githubusercontent.com/u/4779759?v=4","gravatar_id":"","url":"https://api.github.com/users/dapengzhang0","html_url":"https://github.com/dapengzhang0","followers_url":"https://api.github.com/users/dapengzhang0/followers","following_url":"https://api.github.com/users/dapengzhang0/following{/other_user}","gists_url":"https://api.github.com/users/dapengzhang0/gists{/gist_id}","starred_url":"https://api.github.com/users/dapengzhang0/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/dapengzhang0/subscriptions","organizations_url":"https://api.github.com/users/dapengzhang0/orgs","repos_url":"https://api.github.com/users/dapengzhang0/repos","events_url":"https://api.github.com/users/dapengzhang0/events{/privacy}","received_events_url":"https://api.github.com/users/dapengzhang0/received_events","type":"User","site_admin":false},"body":"This is only implementing how subchannel state changes are hooked up to channel state APIs. Debates in state semantics and transition rules for loadbalanced channel such as #2873 are not addressed. Any changes in state semantics and transition rules in the future can be easily adapted.","created_at":"2017-05-18T21:13:31Z","updated_at":"2017-07-19T16:26:58Z","closed_at":null,"merged_at":null,"merge_commit_sha":"9d7fa2317a8eb872346e1316626a96f6c680cae9","assignee":{"login":"zhangkun83","id":9053690,"avatar_url":"https://avatars0.githubusercontent.com/u/9053690?v=4","gravatar_id":"","url":"https://api.github.com/users/zhangkun83","html_url":"https://github.com/zhangkun83","followers_url":"https://api.github.com/users/zhangkun83/followers","following_url":"https://api.github.com/users/zhangkun83/following{/other_user}","gists_url":"https://api.github.com/users/zhangkun83/gists{/gist_id}","starred_url":"https://api.github.com/users/zhangkun83/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/zhangkun83/subscriptions","organizations_url":"https://api.github.com/users/zhangkun83/orgs","repos_url":"https://api.github.com/users/zhangkun83/repos","events_url":"https://api.github.com/users/zhangkun83/events{/privacy}","received_events_url":"https://api.github.com/users/zhangkun83/received_events","type":"User","site_admin":false},"assignees":[{"login":"zhangkun83","id":9053690,"avatar_url":"https://avatars0.githubusercontent.com/u/9053690?v=4","gravatar_id":"","url":"https://api.github.com/users/zhangkun83","html_url":"https://github.com/zhangkun83","followers_url":"https://api.github.com/users/zhangkun83/followers","following_url":"https://api.github.com/users/zhangkun83/following{/other_user}","gists_url":"https://api.github.com/users/zhangkun83/gists{/gist_id}","starred_url":"https://api.github.com/users/zhangkun83/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/zhangkun83/subscriptions","organizations_url":"https://api.github.com/users/zhangkun83/orgs","repos_url":"https://api.github.com/users/zhangkun83/repos","events_url":"https://api.github.com/users/zhangkun83/events{/privacy}","received_events_url":"https://api.github.com/users/zhangkun83/received_events","type":"User","site_admin":false}],"requested_reviewers":[],"milestone":null,"commits_url":"https://api.github.com/repos/grpc/grpc-java/pulls/3019/commits","review_comments_url":"https://api.github.com/repos/grpc/grpc-java/pulls/3019/comments","review_comment_url":"https://api.github.com/repos/grpc/grpc-java/pulls/comments{/number}","comments_url":"https://api.github.com/repos/grpc/grpc-java/issues/3019/comments","statuses_url":"https://api.github.com/repos/grpc/grpc-java/statuses/da151c1df0be687032fb6348df2a3671b05e5459","head":{"label":"dapengzhang0:channelstate","ref":"channelstate","sha":"da151c1df0be687032fb6348df2a3671b05e5459","user":{"login":"dapengzhang0","id":4779759,"avatar_url":"https://avatars2.githubusercontent.com/u/4779759?v=4","gravatar_id":"","url":"https://api.github.com/users/dapengzhang0","html_url":"https://github.com/dapengzhang0","followers_url":"https://api.github.com/users/dapengzhang0/followers","following_url":"https://api.github.com/users/dapengzhang0/following{/other_user}","gists_url":"https://api.github.com/users/dapengzhang0/gists{/gist_id}","starred_url":"https://api.github.com/users/dapengzhang0/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/dapengzhang0/subscriptions","organizations_url":"https://api.github.com/users/dapengzhang0/orgs","repos_url":"https://api.github.com/users/dapengzhang0/repos","events_url":"https://api.github.com/users/dapengzhang0/events{/privacy}","received_events_url":"https://api.github.com/users/dapengzhang0/received_events","type":"User","site_admin":false},"repo":{"id":57351322,"name":"grpc-java","full_name":"dapengzhang0/grpc-java","owner":{"login":"dapengzhang0","id":4779759,"avatar_url":"https://avatars2.githubusercontent.com/u/4779759?v=4","gravatar_id":"","url":"https://api.github.com/users/dapengzhang0","html_url":"https://github.com/dapengzhang0","followers_url":"https://api.github.com/users/dapengzhang0/followers","following_url":"https://api.github.com/users/dapengzhang0/following{/other_user}","gists_url":"https://api.github.com/users/dapengzhang0/gists{/gist_id}","starred_url":"https://api.github.com/users/dapengzhang0/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/dapengzhang0/subscriptions","organizations_url":"https://api.github.com/users/dapengzhang0/orgs","repos_url":"https://api.github.com/users/dapengzhang0/repos","events_url":"https://api.github.com/users/dapengzhang0/events{/privacy}","received_events_url":"https://api.github.com/users/dapengzhang0/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/dapengzhang0/grpc-java","description":"The Java gRPC implementation. HTTP/2 based RPC","fork":true,"url":"https://api.github.com/repos/dapengzhang0/grpc-java","forks_url":"https://api.github.com/repos/dapengzhang0/grpc-java/forks","keys_url":"https://api.github.com/repos/dapengzhang0/grpc-java/keys{/key_id}","collaborators_url":"https://api.github.com/repos/dapengzhang0/grpc-java/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/dapengzhang0/grpc-java/teams","hooks_url":"https://api.github.com/repos/dapengzhang0/grpc-java/hooks","issue_events_url":"https://api.github.com/repos/dapengzhang0/grpc-java/issues/events{/number}","events_url":"https://api.github.com/repos/dapengzhang0/grpc-java/events","assignees_url":"https://api.github.com/repos/dapengzhang0/grpc-java/assignees{/user}","branches_url":"https://api.github.com/repos/dapengzhang0/grpc-java/branches{/branch}","tags_url":"https://api.github.com/repos/dapengzhang0/grpc-java/tags","blobs_url":"https://api.github.com/repos/dapengzhang0/grpc-java/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/dapengzhang0/grpc-java/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/dapengzhang0/grpc-java/git/refs{/sha}","trees_url":"https://api.github.com/repos/dapengzhang0/grpc-java/git/trees{/sha}","statuses_url":"https://api.github.com/repos/dapengzhang0/grpc-java/statuses/{sha}","languages_url":"https://api.github.com/repos/dapengzhang0/grpc-java/languages","stargazers_url":"https://api.github.com/repos/dapengzhang0/grpc-java/stargazers","contributors_url":"https://api.github.com/repos/dapengzhang0/grpc-java/contributors","subscribers_url":"https://api.github.com/repos/dapengzhang0/grpc-java/subscribers","subscription_url":"https://api.github.com/repos/dapengzhang0/grpc-java/subscription","commits_url":"https://api.github.com/repos/dapengzhang0/grpc-java/commits{/sha}","git_commits_url":"https://api.github.com/repos/dapengzhang0/grpc-java/git/commits{/sha}","comments_url":"https://api.github.com/repos/dapengzhang0/grpc-java/comments{/number}","issue_comment_url":"https://api.github.com/repos/dapengzhang0/grpc-java/issues/comments{/number}","contents_url":"https://api.github.com/repos/dapengzhang0/grpc-java/contents/{+path}","compare_url":"https://api.github.com/repos/dapengzhang0/grpc-java/compare/{base}...{head}","merges_url":"https://api.github.com/repos/dapengzhang0/grpc-java/merges","archive_url":"https://api.github.com/repos/dapengzhang0/grpc-java/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/dapengzhang0/grpc-java/downloads","issues_url":"https://api.github.com/repos/dapengzhang0/grpc-java/issues{/number}","pulls_url":"https://api.github.com/repos/dapengzhang0/grpc-java/pulls{/number}","milestones_url":"https://api.github.com/repos/dapengzhang0/grpc-java/milestones{/number}","notifications_url":"https://api.github.com/repos/dapengzhang0/grpc-java/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/dapengzhang0/grpc-java/labels{/name}","releases_url":"https://api.github.com/repos/dapengzhang0/grpc-java/releases{/id}","deployments_url":"https://api.github.com/repos/dapengzhang0/grpc-java/deployments","created_at":"2016-04-29T03:18:59Z","updated_at":"2016-04-29T03:19:01Z","pushed_at":"2017-07-18T17:45:41Z","git_url":"git://github.com/dapengzhang0/grpc-java.git","ssh_url":"git@github.com:dapengzhang0/grpc-java.git","clone_url":"https://github.com/dapengzhang0/grpc-java.git","svn_url":"https://github.com/dapengzhang0/grpc-java","homepage":"http://grpc.io","size":14319,"stargazers_count":0,"watchers_count":0,"language":"Java","has_issues":false,"has_projects":true,"has_downloads":false,"has_wiki":false,"has_pages":false,"forks_count":0,"mirror_url":null,"open_issues_count":0,"forks":0,"open_issues":0,"watchers":0,"default_branch":"master"}},"base":{"label":"grpc:master","ref":"master","sha":"4b94237bb9839d31aa1f77b1c30f824960998e7d","user":{"login":"grpc","id":7802525,"avatar_url":"https://avatars1.githubusercontent.com/u/7802525?v=4","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":27729926,"name":"grpc-java","full_name":"grpc/grpc-java","owner":{"login":"grpc","id":7802525,"avatar_url":"https://avatars1.githubusercontent.com/u/7802525?v=4","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-java","description":"The Java gRPC implementation. HTTP/2 based RPC","fork":false,"url":"https://api.github.com/repos/grpc/grpc-java","forks_url":"https://api.github.com/repos/grpc/grpc-java/forks","keys_url":"https://api.github.com/repos/grpc/grpc-java/keys{/key_id}","collaborators_url":"https://api.github.com/repos/grpc/grpc-java/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/grpc/grpc-java/teams","hooks_url":"https://api.github.com/repos/grpc/grpc-java/hooks","issue_events_url":"https://api.github.com/repos/grpc/grpc-java/issues/events{/number}","events_url":"https://api.github.com/repos/grpc/grpc-java/events","assignees_url":"https://api.github.com/repos/grpc/grpc-java/assignees{/user}","branches_url":"https://api.github.com/repos/grpc/grpc-java/branches{/branch}","tags_url":"https://api.github.com/repos/grpc/grpc-java/tags","blobs_url":"https://api.github.com/repos/grpc/grpc-java/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/grpc/grpc-java/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/grpc/grpc-java/git/refs{/sha}","trees_url":"https://api.github.com/repos/grpc/grpc-java/git/trees{/sha}","statuses_url":"https://api.github.com/repos/grpc/grpc-java/statuses/{sha}","languages_url":"https://api.github.com/repos/grpc/grpc-java/languages","stargazers_url":"https://api.github.com/repos/grpc/grpc-java/stargazers","contributors_url":"https://api.github.com/repos/grpc/grpc-java/contributors","subscribers_url":"https://api.github.com/repos/grpc/grpc-java/subscribers","subscription_url":"https://api.github.com/repos/grpc/grpc-java/subscription","commits_url":"https://api.github.com/repos/grpc/grpc-java/commits{/sha}","git_commits_url":"https://api.github.com/repos/grpc/grpc-java/git/commits{/sha}","comments_url":"https://api.github.com/repos/grpc/grpc-java/comments{/number}","issue_comment_url":"https://api.github.com/repos/grpc/grpc-java/issues/comments{/number}","contents_url":"https://api.github.com/repos/grpc/grpc-java/contents/{+path}","compare_url":"https://api.github.com/repos/grpc/grpc-java/compare/{base}...{head}","merges_url":"https://api.github.com/repos/grpc/grpc-java/merges","archive_url":"https://api.github.com/repos/grpc/grpc-java/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/grpc/grpc-java/downloads","issues_url":"https://api.github.com/repos/grpc/grpc-java/issues{/number}","pulls_url":"https://api.github.com/repos/grpc/grpc-java/pulls{/number}","milestones_url":"https://api.github.com/repos/grpc/grpc-java/milestones{/number}","notifications_url":"https://api.github.com/repos/grpc/grpc-java/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/grpc/grpc-java/labels{/name}","releases_url":"https://api.github.com/repos/grpc/grpc-java/releases{/id}","deployments_url":"https://api.github.com/repos/grpc/grpc-java/deployments","created_at":"2014-12-08T19:00:00Z","updated_at":"2017-07-19T08:34:51Z","pushed_at":"2017-07-19T01:03:43Z","git_url":"git://github.com/grpc/grpc-java.git","ssh_url":"git@github.com:grpc/grpc-java.git","clone_url":"https://github.com/grpc/grpc-java.git","svn_url":"https://github.com/grpc/grpc-java","homepage":"http://grpc.io","size":16564,"stargazers_count":2938,"watchers_count":2938,"language":"Java","has_issues":true,"has_projects":true,"has_downloads":false,"has_wiki":false,"has_pages":true,"forks_count":937,"mirror_url":null,"open_issues_count":343,"forks":937,"open_issues":343,"watchers":2938,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/grpc/grpc-java/pulls/3019"},"html":{"href":"https://github.com/grpc/grpc-java/pull/3019"},"issue":{"href":"https://api.github.com/repos/grpc/grpc-java/issues/3019"},"comments":{"href":"https://api.github.com/repos/grpc/grpc-java/issues/3019/comments"},"review_comments":{"href":"https://api.github.com/repos/grpc/grpc-java/pulls/3019/comments"},"review_comment":{"href":"https://api.github.com/repos/grpc/grpc-java/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/grpc/grpc-java/pulls/3019/commits"},"statuses":{"href":"https://api.github.com/repos/grpc/grpc-java/statuses/da151c1df0be687032fb6348df2a3671b05e5459"}}}}
{"actor":{"display_login":"dapengzhang0"}}