diff --git a/BUILD b/BUILD
index b24be86276eeee0160abcc10a84b7c4dbaf2ea46..ce7ad0335f3f3381c4c13c3d1f28b27b62233fa9 100644
--- a/BUILD
+++ b/BUILD
@@ -190,6 +190,7 @@ cc_library(
     "src/core/lib/iomgr/pollset_set.h",
     "src/core/lib/iomgr/pollset_set_windows.h",
     "src/core/lib/iomgr/pollset_windows.h",
+    "src/core/lib/iomgr/pops.h",
     "src/core/lib/iomgr/resolve_address.h",
     "src/core/lib/iomgr/sockaddr.h",
     "src/core/lib/iomgr/sockaddr_posix.h",
@@ -287,7 +288,6 @@ cc_library(
     "src/core/ext/client_config/uri_parser.h",
     "src/core/ext/lb_policy/grpclb/load_balancer_api.h",
     "src/core/ext/lb_policy/grpclb/proto/grpc/lb/v0/load_balancer.pb.h",
-    "src/core/ext/lb_policy/common.h",
     "src/core/ext/census/aggregation.h",
     "src/core/ext/census/census_interface.h",
     "src/core/ext/census/census_rpc_stats.h",
@@ -322,6 +322,7 @@ cc_library(
     "src/core/lib/iomgr/iomgr_windows.c",
     "src/core/lib/iomgr/pollset_set_windows.c",
     "src/core/lib/iomgr/pollset_windows.c",
+    "src/core/lib/iomgr/pops.c",
     "src/core/lib/iomgr/resolve_address_posix.c",
     "src/core/lib/iomgr/resolve_address_windows.c",
     "src/core/lib/iomgr/sockaddr_utils.c",
@@ -442,7 +443,6 @@ cc_library(
     "src/core/ext/transport/chttp2/client/insecure/channel_create.c",
     "src/core/ext/lb_policy/grpclb/load_balancer_api.c",
     "src/core/ext/lb_policy/grpclb/proto/grpc/lb/v0/load_balancer.pb.c",
-    "src/core/ext/lb_policy/common.c",
     "src/core/ext/lb_policy/pick_first/pick_first.c",
     "src/core/ext/lb_policy/round_robin/round_robin.c",
     "src/core/ext/resolver/dns/native/dns_resolver.c",
@@ -537,6 +537,7 @@ cc_library(
     "src/core/lib/iomgr/pollset_set.h",
     "src/core/lib/iomgr/pollset_set_windows.h",
     "src/core/lib/iomgr/pollset_windows.h",
+    "src/core/lib/iomgr/pops.h",
     "src/core/lib/iomgr/resolve_address.h",
     "src/core/lib/iomgr/sockaddr.h",
     "src/core/lib/iomgr/sockaddr_posix.h",
@@ -620,7 +621,6 @@ cc_library(
     "src/core/ext/client_config/uri_parser.h",
     "src/core/ext/lb_policy/grpclb/load_balancer_api.h",
     "src/core/ext/lb_policy/grpclb/proto/grpc/lb/v0/load_balancer.pb.h",
-    "src/core/ext/lb_policy/common.h",
     "src/core/ext/census/aggregation.h",
     "src/core/ext/census/census_interface.h",
     "src/core/ext/census/census_rpc_stats.h",
@@ -656,6 +656,7 @@ cc_library(
     "src/core/lib/iomgr/iomgr_windows.c",
     "src/core/lib/iomgr/pollset_set_windows.c",
     "src/core/lib/iomgr/pollset_windows.c",
+    "src/core/lib/iomgr/pops.c",
     "src/core/lib/iomgr/resolve_address_posix.c",
     "src/core/lib/iomgr/resolve_address_windows.c",
     "src/core/lib/iomgr/sockaddr_utils.c",
@@ -757,7 +758,6 @@ cc_library(
     "src/core/ext/resolver/sockaddr/sockaddr_resolver.c",
     "src/core/ext/lb_policy/grpclb/load_balancer_api.c",
     "src/core/ext/lb_policy/grpclb/proto/grpc/lb/v0/load_balancer.pb.c",
-    "src/core/ext/lb_policy/common.c",
     "src/core/ext/lb_policy/pick_first/pick_first.c",
     "src/core/ext/lb_policy/round_robin/round_robin.c",
     "src/core/ext/census/context.c",
@@ -1342,6 +1342,7 @@ objc_library(
     "src/core/lib/iomgr/iomgr_windows.c",
     "src/core/lib/iomgr/pollset_set_windows.c",
     "src/core/lib/iomgr/pollset_windows.c",
+    "src/core/lib/iomgr/pops.c",
     "src/core/lib/iomgr/resolve_address_posix.c",
     "src/core/lib/iomgr/resolve_address_windows.c",
     "src/core/lib/iomgr/sockaddr_utils.c",
@@ -1462,7 +1463,6 @@ objc_library(
     "src/core/ext/transport/chttp2/client/insecure/channel_create.c",
     "src/core/ext/lb_policy/grpclb/load_balancer_api.c",
     "src/core/ext/lb_policy/grpclb/proto/grpc/lb/v0/load_balancer.pb.c",
-    "src/core/ext/lb_policy/common.c",
     "src/core/ext/lb_policy/pick_first/pick_first.c",
     "src/core/ext/lb_policy/round_robin/round_robin.c",
     "src/core/ext/resolver/dns/native/dns_resolver.c",
@@ -1536,6 +1536,7 @@ objc_library(
     "src/core/lib/iomgr/pollset_set.h",
     "src/core/lib/iomgr/pollset_set_windows.h",
     "src/core/lib/iomgr/pollset_windows.h",
+    "src/core/lib/iomgr/pops.h",
     "src/core/lib/iomgr/resolve_address.h",
     "src/core/lib/iomgr/sockaddr.h",
     "src/core/lib/iomgr/sockaddr_posix.h",
@@ -1633,7 +1634,6 @@ objc_library(
     "src/core/ext/client_config/uri_parser.h",
     "src/core/ext/lb_policy/grpclb/load_balancer_api.h",
     "src/core/ext/lb_policy/grpclb/proto/grpc/lb/v0/load_balancer.pb.h",
-    "src/core/ext/lb_policy/common.h",
     "src/core/ext/census/aggregation.h",
     "src/core/ext/census/census_interface.h",
     "src/core/ext/census/census_rpc_stats.h",
diff --git a/Makefile b/Makefile
index 614c46e580877c958d857c730e7feea2da986790..08a74af9950cdda161b71a1769056418dd8eef87 100644
--- a/Makefile
+++ b/Makefile
@@ -2505,6 +2505,7 @@ LIBGRPC_SRC = \
     src/core/lib/iomgr/iomgr_windows.c \
     src/core/lib/iomgr/pollset_set_windows.c \
     src/core/lib/iomgr/pollset_windows.c \
+    src/core/lib/iomgr/pops.c \
     src/core/lib/iomgr/resolve_address_posix.c \
     src/core/lib/iomgr/resolve_address_windows.c \
     src/core/lib/iomgr/sockaddr_utils.c \
@@ -2628,7 +2629,6 @@ LIBGRPC_SRC = \
     third_party/nanopb/pb_common.c \
     third_party/nanopb/pb_decode.c \
     third_party/nanopb/pb_encode.c \
-    src/core/ext/lb_policy/common.c \
     src/core/ext/lb_policy/pick_first/pick_first.c \
     src/core/ext/lb_policy/round_robin/round_robin.c \
     src/core/ext/resolver/dns/native/dns_resolver.c \
@@ -2847,6 +2847,7 @@ LIBGRPC_UNSECURE_SRC = \
     src/core/lib/iomgr/iomgr_windows.c \
     src/core/lib/iomgr/pollset_set_windows.c \
     src/core/lib/iomgr/pollset_windows.c \
+    src/core/lib/iomgr/pops.c \
     src/core/lib/iomgr/resolve_address_posix.c \
     src/core/lib/iomgr/resolve_address_windows.c \
     src/core/lib/iomgr/sockaddr_utils.c \
@@ -2951,7 +2952,6 @@ LIBGRPC_UNSECURE_SRC = \
     third_party/nanopb/pb_common.c \
     third_party/nanopb/pb_decode.c \
     third_party/nanopb/pb_encode.c \
-    src/core/ext/lb_policy/common.c \
     src/core/ext/lb_policy/pick_first/pick_first.c \
     src/core/ext/lb_policy/round_robin/round_robin.c \
     src/core/ext/census/context.c \
diff --git a/binding.gyp b/binding.gyp
index 92ab280da07a2433612845021bcad82ce142cbfa..348c05e5aa61844f918ec42856bd2261c18c0f43 100644
--- a/binding.gyp
+++ b/binding.gyp
@@ -591,6 +591,7 @@
         'src/core/lib/iomgr/iomgr_windows.c',
         'src/core/lib/iomgr/pollset_set_windows.c',
         'src/core/lib/iomgr/pollset_windows.c',
+        'src/core/lib/iomgr/pops.c',
         'src/core/lib/iomgr/resolve_address_posix.c',
         'src/core/lib/iomgr/resolve_address_windows.c',
         'src/core/lib/iomgr/sockaddr_utils.c',
@@ -714,7 +715,6 @@
         'third_party/nanopb/pb_common.c',
         'third_party/nanopb/pb_decode.c',
         'third_party/nanopb/pb_encode.c',
-        'src/core/ext/lb_policy/common.c',
         'src/core/ext/lb_policy/pick_first/pick_first.c',
         'src/core/ext/lb_policy/round_robin/round_robin.c',
         'src/core/ext/resolver/dns/native/dns_resolver.c',
diff --git a/build.yaml b/build.yaml
index 13e44e78b2abb77ec49231e0331fb82d8d1fad7e..834a53bf0eb8de1bec24076669a11f74ee7eecf4 100644
--- a/build.yaml
+++ b/build.yaml
@@ -177,6 +177,7 @@ filegroups:
   - src/core/lib/iomgr/pollset_set.h
   - src/core/lib/iomgr/pollset_set_windows.h
   - src/core/lib/iomgr/pollset_windows.h
+  - src/core/lib/iomgr/pops.h
   - src/core/lib/iomgr/resolve_address.h
   - src/core/lib/iomgr/sockaddr.h
   - src/core/lib/iomgr/sockaddr_posix.h
@@ -249,6 +250,7 @@ filegroups:
   - src/core/lib/iomgr/iomgr_windows.c
   - src/core/lib/iomgr/pollset_set_windows.c
   - src/core/lib/iomgr/pollset_windows.c
+  - src/core/lib/iomgr/pops.c
   - src/core/lib/iomgr/resolve_address_posix.c
   - src/core/lib/iomgr/resolve_address_windows.c
   - src/core/lib/iomgr/sockaddr_utils.c
@@ -358,13 +360,6 @@ filegroups:
   - include/grpc/impl/codegen/status.h
   uses:
   - gpr_codegen
-- name: grpc_lb_policy_common
-  headers:
-  - src/core/ext/lb_policy/common.h
-  src:
-  - src/core/ext/lb_policy/common.c
-  uses:
-  - grpc_base
 - name: grpc_lb_policy_grpclb
   headers:
   - src/core/ext/lb_policy/grpclb/load_balancer_api.h
@@ -376,7 +371,6 @@ filegroups:
   - grpc_base
   - grpc_client_config
   - nanopb
-  - grpc_lb_policy_common
 - name: grpc_lb_policy_pick_first
   src:
   - src/core/ext/lb_policy/pick_first/pick_first.c
@@ -384,7 +378,6 @@ filegroups:
   uses:
   - grpc_base
   - grpc_client_config
-  - grpc_lb_policy_common
 - name: grpc_lb_policy_round_robin
   src:
   - src/core/ext/lb_policy/round_robin/round_robin.c
@@ -392,7 +385,6 @@ filegroups:
   uses:
   - grpc_base
   - grpc_client_config
-  - grpc_lb_policy_common
 - name: grpc_resolver_dns_native
   src:
   - src/core/ext/resolver/dns/native/dns_resolver.c
diff --git a/config.m4 b/config.m4
index ea641d6cfaea7b64aaf8517e314228e4ffcc9caf..9bbbd7140d851833cf3fdfbb0e3ece5b945aed0d 100644
--- a/config.m4
+++ b/config.m4
@@ -110,6 +110,7 @@ if test "$PHP_GRPC" != "no"; then
     src/core/lib/iomgr/iomgr_windows.c \
     src/core/lib/iomgr/pollset_set_windows.c \
     src/core/lib/iomgr/pollset_windows.c \
+    src/core/lib/iomgr/pops.c \
     src/core/lib/iomgr/resolve_address_posix.c \
     src/core/lib/iomgr/resolve_address_windows.c \
     src/core/lib/iomgr/sockaddr_utils.c \
@@ -233,7 +234,6 @@ if test "$PHP_GRPC" != "no"; then
     third_party/nanopb/pb_common.c \
     third_party/nanopb/pb_decode.c \
     third_party/nanopb/pb_encode.c \
-    src/core/ext/lb_policy/common.c \
     src/core/ext/lb_policy/pick_first/pick_first.c \
     src/core/ext/lb_policy/round_robin/round_robin.c \
     src/core/ext/resolver/dns/native/dns_resolver.c \
@@ -555,7 +555,6 @@ if test "$PHP_GRPC" != "no"; then
   PHP_ADD_BUILD_DIR($ext_builddir/src/boringssl)
   PHP_ADD_BUILD_DIR($ext_builddir/src/core/ext/census)
   PHP_ADD_BUILD_DIR($ext_builddir/src/core/ext/client_config)
-  PHP_ADD_BUILD_DIR($ext_builddir/src/core/ext/lb_policy)
   PHP_ADD_BUILD_DIR($ext_builddir/src/core/ext/lb_policy/grpclb)
   PHP_ADD_BUILD_DIR($ext_builddir/src/core/ext/lb_policy/grpclb/proto/grpc/lb/v0)
   PHP_ADD_BUILD_DIR($ext_builddir/src/core/ext/lb_policy/pick_first)
diff --git a/gRPC.podspec b/gRPC.podspec
index 67079ec2d6638bac5ea3220d13516dcc318a5bfd..fef73cae0ce27985b05bdd0fb647ad68b28eb7f9 100644
--- a/gRPC.podspec
+++ b/gRPC.podspec
@@ -192,6 +192,7 @@ Pod::Spec.new do |s|
                       'src/core/lib/iomgr/pollset_set.h',
                       'src/core/lib/iomgr/pollset_set_windows.h',
                       'src/core/lib/iomgr/pollset_windows.h',
+                      'src/core/lib/iomgr/pops.h',
                       'src/core/lib/iomgr/resolve_address.h',
                       'src/core/lib/iomgr/sockaddr.h',
                       'src/core/lib/iomgr/sockaddr_posix.h',
@@ -293,7 +294,6 @@ Pod::Spec.new do |s|
                       'third_party/nanopb/pb_common.h',
                       'third_party/nanopb/pb_decode.h',
                       'third_party/nanopb/pb_encode.h',
-                      'src/core/ext/lb_policy/common.h',
                       'src/core/ext/census/aggregation.h',
                       'src/core/ext/census/census_interface.h',
                       'src/core/ext/census/census_rpc_stats.h',
@@ -356,6 +356,7 @@ Pod::Spec.new do |s|
                       'src/core/lib/iomgr/iomgr_windows.c',
                       'src/core/lib/iomgr/pollset_set_windows.c',
                       'src/core/lib/iomgr/pollset_windows.c',
+                      'src/core/lib/iomgr/pops.c',
                       'src/core/lib/iomgr/resolve_address_posix.c',
                       'src/core/lib/iomgr/resolve_address_windows.c',
                       'src/core/lib/iomgr/sockaddr_utils.c',
@@ -479,7 +480,6 @@ Pod::Spec.new do |s|
                       'third_party/nanopb/pb_common.c',
                       'third_party/nanopb/pb_decode.c',
                       'third_party/nanopb/pb_encode.c',
-                      'src/core/ext/lb_policy/common.c',
                       'src/core/ext/lb_policy/pick_first/pick_first.c',
                       'src/core/ext/lb_policy/round_robin/round_robin.c',
                       'src/core/ext/resolver/dns/native/dns_resolver.c',
@@ -536,6 +536,7 @@ Pod::Spec.new do |s|
                               'src/core/lib/iomgr/pollset_set.h',
                               'src/core/lib/iomgr/pollset_set_windows.h',
                               'src/core/lib/iomgr/pollset_windows.h',
+                              'src/core/lib/iomgr/pops.h',
                               'src/core/lib/iomgr/resolve_address.h',
                               'src/core/lib/iomgr/sockaddr.h',
                               'src/core/lib/iomgr/sockaddr_posix.h',
@@ -637,7 +638,6 @@ Pod::Spec.new do |s|
                               'third_party/nanopb/pb_common.h',
                               'third_party/nanopb/pb_decode.h',
                               'third_party/nanopb/pb_encode.h',
-                              'src/core/ext/lb_policy/common.h',
                               'src/core/ext/census/aggregation.h',
                               'src/core/ext/census/census_interface.h',
                               'src/core/ext/census/census_rpc_stats.h',
diff --git a/grpc.gemspec b/grpc.gemspec
index f34bc51ffce5d546aac7f88172d9b99b037a937a..5b9de7278f7b51c306a9717ab05a82bb535cf3e3 100755
--- a/grpc.gemspec
+++ b/grpc.gemspec
@@ -200,6 +200,7 @@ Gem::Specification.new do |s|
   s.files += %w( src/core/lib/iomgr/pollset_set.h )
   s.files += %w( src/core/lib/iomgr/pollset_set_windows.h )
   s.files += %w( src/core/lib/iomgr/pollset_windows.h )
+  s.files += %w( src/core/lib/iomgr/pops.h )
   s.files += %w( src/core/lib/iomgr/resolve_address.h )
   s.files += %w( src/core/lib/iomgr/sockaddr.h )
   s.files += %w( src/core/lib/iomgr/sockaddr_posix.h )
@@ -301,7 +302,6 @@ Gem::Specification.new do |s|
   s.files += %w( third_party/nanopb/pb_common.h )
   s.files += %w( third_party/nanopb/pb_decode.h )
   s.files += %w( third_party/nanopb/pb_encode.h )
-  s.files += %w( src/core/ext/lb_policy/common.h )
   s.files += %w( src/core/ext/census/aggregation.h )
   s.files += %w( src/core/ext/census/census_interface.h )
   s.files += %w( src/core/ext/census/census_rpc_stats.h )
@@ -336,6 +336,7 @@ Gem::Specification.new do |s|
   s.files += %w( src/core/lib/iomgr/iomgr_windows.c )
   s.files += %w( src/core/lib/iomgr/pollset_set_windows.c )
   s.files += %w( src/core/lib/iomgr/pollset_windows.c )
+  s.files += %w( src/core/lib/iomgr/pops.c )
   s.files += %w( src/core/lib/iomgr/resolve_address_posix.c )
   s.files += %w( src/core/lib/iomgr/resolve_address_windows.c )
   s.files += %w( src/core/lib/iomgr/sockaddr_utils.c )
@@ -459,7 +460,6 @@ Gem::Specification.new do |s|
   s.files += %w( third_party/nanopb/pb_common.c )
   s.files += %w( third_party/nanopb/pb_decode.c )
   s.files += %w( third_party/nanopb/pb_encode.c )
-  s.files += %w( src/core/ext/lb_policy/common.c )
   s.files += %w( src/core/ext/lb_policy/pick_first/pick_first.c )
   s.files += %w( src/core/ext/lb_policy/round_robin/round_robin.c )
   s.files += %w( src/core/ext/resolver/dns/native/dns_resolver.c )
diff --git a/package.xml b/package.xml
index a27463ad3666fdbeb68c83104e5f8fd01531a704..289fb4e31bdeabbee68c35b5fa0b25334da3cdf5 100644
--- a/package.xml
+++ b/package.xml
@@ -207,6 +207,7 @@
     <file baseinstalldir="/" name="src/core/lib/iomgr/pollset_set.h" role="src" />
     <file baseinstalldir="/" name="src/core/lib/iomgr/pollset_set_windows.h" role="src" />
     <file baseinstalldir="/" name="src/core/lib/iomgr/pollset_windows.h" role="src" />
+    <file baseinstalldir="/" name="src/core/lib/iomgr/pops.h" role="src" />
     <file baseinstalldir="/" name="src/core/lib/iomgr/resolve_address.h" role="src" />
     <file baseinstalldir="/" name="src/core/lib/iomgr/sockaddr.h" role="src" />
     <file baseinstalldir="/" name="src/core/lib/iomgr/sockaddr_posix.h" role="src" />
@@ -308,7 +309,6 @@
     <file baseinstalldir="/" name="third_party/nanopb/pb_common.h" role="src" />
     <file baseinstalldir="/" name="third_party/nanopb/pb_decode.h" role="src" />
     <file baseinstalldir="/" name="third_party/nanopb/pb_encode.h" role="src" />
-    <file baseinstalldir="/" name="src/core/ext/lb_policy/common.h" role="src" />
     <file baseinstalldir="/" name="src/core/ext/census/aggregation.h" role="src" />
     <file baseinstalldir="/" name="src/core/ext/census/census_interface.h" role="src" />
     <file baseinstalldir="/" name="src/core/ext/census/census_rpc_stats.h" role="src" />
@@ -343,6 +343,7 @@
     <file baseinstalldir="/" name="src/core/lib/iomgr/iomgr_windows.c" role="src" />
     <file baseinstalldir="/" name="src/core/lib/iomgr/pollset_set_windows.c" role="src" />
     <file baseinstalldir="/" name="src/core/lib/iomgr/pollset_windows.c" role="src" />
+    <file baseinstalldir="/" name="src/core/lib/iomgr/pops.c" role="src" />
     <file baseinstalldir="/" name="src/core/lib/iomgr/resolve_address_posix.c" role="src" />
     <file baseinstalldir="/" name="src/core/lib/iomgr/resolve_address_windows.c" role="src" />
     <file baseinstalldir="/" name="src/core/lib/iomgr/sockaddr_utils.c" role="src" />
@@ -466,7 +467,6 @@
     <file baseinstalldir="/" name="third_party/nanopb/pb_common.c" role="src" />
     <file baseinstalldir="/" name="third_party/nanopb/pb_decode.c" role="src" />
     <file baseinstalldir="/" name="third_party/nanopb/pb_encode.c" role="src" />
-    <file baseinstalldir="/" name="src/core/ext/lb_policy/common.c" role="src" />
     <file baseinstalldir="/" name="src/core/ext/lb_policy/pick_first/pick_first.c" role="src" />
     <file baseinstalldir="/" name="src/core/ext/lb_policy/round_robin/round_robin.c" role="src" />
     <file baseinstalldir="/" name="src/core/ext/resolver/dns/native/dns_resolver.c" role="src" />
diff --git a/src/core/ext/client_config/client_channel.c b/src/core/ext/client_config/client_channel.c
index 4fd1ef41aa7f225fd879d0537a8931c5c50f5c98..94360793af864b0e67c68fdc4c1e4e39e9ae7278 100644
--- a/src/core/ext/client_config/client_channel.c
+++ b/src/core/ext/client_config/client_channel.c
@@ -377,9 +377,7 @@ static int cc_pick_subchannel(grpc_exec_ctx *exec_ctx, void *elemp,
     int r;
     GRPC_LB_POLICY_REF(lb_policy, "cc_pick_subchannel");
     gpr_mu_unlock(&chand->mu_config);
-    GPR_ASSERT((calld->pollset != NULL) + (calld->pollset_set != NULL) == 1);
-    r = grpc_lb_policy_pick(exec_ctx, lb_policy, calld->pollset,
-                            calld->pollset_set, initial_metadata,
+    r = grpc_lb_policy_pick(exec_ctx, lb_policy, calld->pops, initial_metadata,
                             initial_metadata_flags, connected_subchannel,
                             on_ready);
     GRPC_LB_POLICY_UNREF(exec_ctx, lb_policy, "cc_pick_subchannel");
@@ -462,15 +460,11 @@ static void destroy_channel_elem(grpc_exec_ctx *exec_ctx,
   gpr_mu_destroy(&chand->mu_config);
 }
 
-static void cc_set_pollset_or_pollset_set(
-    grpc_exec_ctx *exec_ctx, grpc_call_element *elem, grpc_pollset *pollset,
-    grpc_pollset_set *pollset_set_alternative) {
-  GPR_ASSERT((pollset == NULL) + (pollset_set_alternative == NULL) == 1);
-  GPR_ASSERT(pollset != NULL || pollset_set_alternative != NULL);
-
+static void cc_set_pollset_or_pollset_set(grpc_exec_ctx *exec_ctx,
+                                          grpc_call_element *elem,
+                                          grpc_pops *pops) {
   call_data *calld = elem->call_data;
-  calld->pollset = pollset;
-  calld->pollset_set = pollset_set_alternative;
+  calld->pops = pops;
 }
 
 const grpc_channel_filter grpc_client_channel_filter = {
diff --git a/src/core/ext/client_config/lb_policy.c b/src/core/ext/client_config/lb_policy.c
index 96342c7c7d841b3bc0802568fb4b29280ba512e1..8bf54d2abc6176a889a54fd009628bcc76248cd8 100644
--- a/src/core/ext/client_config/lb_policy.c
+++ b/src/core/ext/client_config/lb_policy.c
@@ -99,14 +99,11 @@ void grpc_lb_policy_weak_unref(grpc_exec_ctx *exec_ctx,
 }
 
 int grpc_lb_policy_pick(grpc_exec_ctx *exec_ctx, grpc_lb_policy *policy,
-                        grpc_pollset *pollset,
-                        grpc_pollset_set *pollset_set_alternative,
-                        grpc_metadata_batch *initial_metadata,
+                        grpc_pops *pops, grpc_metadata_batch *initial_metadata,
                         uint32_t initial_metadata_flags,
                         grpc_connected_subchannel **target,
                         grpc_closure *on_complete) {
-  return policy->vtable->pick(exec_ctx, policy, pollset,
-                              pollset_set_alternative, initial_metadata,
+  return policy->vtable->pick(exec_ctx, policy, pops, initial_metadata,
                               initial_metadata_flags, target, on_complete);
 }
 
diff --git a/src/core/ext/client_config/lb_policy.h b/src/core/ext/client_config/lb_policy.h
index e9bb1850e79d76d8527e6ff182108c1d955ac6a3..e4ae016e4aaf7f7bf81a81a4b268721c9b0cb23d 100644
--- a/src/core/ext/client_config/lb_policy.h
+++ b/src/core/ext/client_config/lb_policy.h
@@ -35,6 +35,7 @@
 #define GRPC_CORE_EXT_CLIENT_CONFIG_LB_POLICY_H
 
 #include "src/core/ext/client_config/subchannel.h"
+#include "src/core/lib/iomgr/pops.h"
 #include "src/core/lib/transport/connectivity_state.h"
 
 /** A load balancing policy: specified by a vtable and a struct (which
@@ -58,8 +59,7 @@ struct grpc_lb_policy_vtable {
   void (*shutdown)(grpc_exec_ctx *exec_ctx, grpc_lb_policy *policy);
 
   /** implement grpc_lb_policy_pick */
-  int (*pick)(grpc_exec_ctx *exec_ctx, grpc_lb_policy *policy,
-              grpc_pollset *pollset, grpc_pollset_set *pollset_set_alternative,
+  int (*pick)(grpc_exec_ctx *exec_ctx, grpc_lb_policy *policy, grpc_pops *pops,
               grpc_metadata_batch *initial_metadata,
               uint32_t initial_metadata_flags,
               grpc_connected_subchannel **target, grpc_closure *on_complete);
@@ -125,9 +125,7 @@ void grpc_lb_policy_init(grpc_lb_policy *policy,
     \a target.
     Picking can be asynchronous. Any IO should be done under \a pollset. */
 int grpc_lb_policy_pick(grpc_exec_ctx *exec_ctx, grpc_lb_policy *policy,
-                        grpc_pollset *pollset,
-                        grpc_pollset_set *pollset_set_alternative,
-                        grpc_metadata_batch *initial_metadata,
+                        grpc_pops *pops, grpc_metadata_batch *initial_metadata,
                         uint32_t initial_metadata_flags,
                         grpc_connected_subchannel **target,
                         grpc_closure *on_complete);
diff --git a/src/core/ext/client_config/subchannel.c b/src/core/ext/client_config/subchannel.c
index 0ef80ff47e8b71bf8a6ae7a4c4c3e192945e05a4..146ee5e6627c91782aa64dde285cdcbdc0ae627e 100644
--- a/src/core/ext/client_config/subchannel.c
+++ b/src/core/ext/client_config/subchannel.c
@@ -682,8 +682,7 @@ grpc_connected_subchannel *grpc_subchannel_get_connected_subchannel(
 }
 
 grpc_subchannel_call *grpc_connected_subchannel_create_call(
-    grpc_exec_ctx *exec_ctx, grpc_connected_subchannel *con,
-    grpc_pollset *pollset) {
+    grpc_exec_ctx *exec_ctx, grpc_connected_subchannel *con, grpc_pops *pops) {
   grpc_channel_stack *chanstk = CHANNEL_STACK_FROM_CONNECTION(con);
   grpc_subchannel_call *call =
       gpr_malloc(sizeof(grpc_subchannel_call) + chanstk->call_stack_size);
@@ -692,7 +691,7 @@ grpc_subchannel_call *grpc_connected_subchannel_create_call(
   GRPC_CONNECTED_SUBCHANNEL_REF(con, "subchannel_call");
   grpc_call_stack_init(exec_ctx, chanstk, 1, subchannel_call_destroy, call,
                        NULL, NULL, callstk);
-  grpc_call_stack_set_pollset_or_pollset_set(exec_ctx, callstk, pollset, NULL);
+  grpc_call_stack_set_pollset_or_pollset_set(exec_ctx, callstk, pops);
   return call;
 }
 
diff --git a/src/core/ext/client_config/subchannel.h b/src/core/ext/client_config/subchannel.h
index 0765a544e8b7070ee35d58e009aeb35fb252b417..fe9ece08f184f7a3ace65758c28b6f240f25a88f 100644
--- a/src/core/ext/client_config/subchannel.h
+++ b/src/core/ext/client_config/subchannel.h
@@ -36,6 +36,7 @@
 
 #include "src/core/ext/client_config/connector.h"
 #include "src/core/lib/channel/channel_stack.h"
+#include "src/core/lib/iomgr/pops.h"
 #include "src/core/lib/transport/connectivity_state.h"
 
 /** A (sub-)channel that knows how to connect to exactly one target
@@ -109,7 +110,7 @@ void grpc_subchannel_call_unref(grpc_exec_ctx *exec_ctx,
 /** construct a subchannel call */
 grpc_subchannel_call *grpc_connected_subchannel_create_call(
     grpc_exec_ctx *exec_ctx, grpc_connected_subchannel *connected_subchannel,
-    grpc_pollset *pollset);
+    grpc_pops *pops);
 
 /** process a transport level op */
 void grpc_connected_subchannel_process_transport_op(
diff --git a/src/core/ext/client_config/subchannel_call_holder.c b/src/core/ext/client_config/subchannel_call_holder.c
index 96a4593b3c216307bc56d0cf6ddbeb49f2f0f91b..02f63acd3bd5c39b849090a8296941cf4271a515 100644
--- a/src/core/ext/client_config/subchannel_call_holder.c
+++ b/src/core/ext/client_config/subchannel_call_holder.c
@@ -68,7 +68,7 @@ void grpc_subchannel_call_holder_init(
   holder->waiting_ops_capacity = 0;
   holder->creation_phase = GRPC_SUBCHANNEL_CALL_HOLDER_NOT_CREATING;
   holder->owning_call = owning_call;
-  holder->pollset_set = NULL;
+  holder->pops = NULL;
 }
 
 void grpc_subchannel_call_holder_destroy(grpc_exec_ctx *exec_ctx,
@@ -158,7 +158,7 @@ retry:
     gpr_atm_rel_store(
         &holder->subchannel_call,
         (gpr_atm)(uintptr_t)grpc_connected_subchannel_create_call(
-            exec_ctx, holder->connected_subchannel, holder->pollset));
+            exec_ctx, holder->connected_subchannel, holder->pops));
     retry_waiting_locked(exec_ctx, holder);
     goto retry;
   }
@@ -183,7 +183,7 @@ static void subchannel_ready(grpc_exec_ctx *exec_ctx, void *arg, bool success) {
     gpr_atm_rel_store(
         &holder->subchannel_call,
         (gpr_atm)(uintptr_t)grpc_connected_subchannel_create_call(
-            exec_ctx, holder->connected_subchannel, holder->pollset));
+            exec_ctx, holder->connected_subchannel, holder->pops));
     retry_waiting_locked(exec_ctx, holder);
   }
   gpr_mu_unlock(&holder->mu);
diff --git a/src/core/ext/client_config/subchannel_call_holder.h b/src/core/ext/client_config/subchannel_call_holder.h
index 6ed011ff8804f05d7ac1ca61571a9cd1980f42d0..81ac5f5b97211eb4516d0b3ad4ce84db96b931fc 100644
--- a/src/core/ext/client_config/subchannel_call_holder.h
+++ b/src/core/ext/client_config/subchannel_call_holder.h
@@ -35,6 +35,7 @@
 #define GRPC_CORE_EXT_CLIENT_CONFIG_SUBCHANNEL_CALL_HOLDER_H
 
 #include "src/core/ext/client_config/subchannel.h"
+#include "src/core/lib/iomgr/pops.h"
 
 /** Pick a subchannel for grpc_subchannel_call_holder;
     Return 1 if subchannel is available immediately (in which case on_ready
@@ -71,8 +72,7 @@ typedef struct grpc_subchannel_call_holder {
 
   grpc_subchannel_call_holder_creation_phase creation_phase;
   grpc_connected_subchannel *connected_subchannel;
-  grpc_pollset *pollset;
-  grpc_pollset_set *pollset_set;
+  grpc_pops *pops;
 
   grpc_transport_stream_op *waiting_ops;
   size_t waiting_ops_count;
diff --git a/src/core/ext/lb_policy/common.c b/src/core/ext/lb_policy/common.c
deleted file mode 100644
index bfb6aace8778ae526f3e40f41257d5b70ca7292e..0000000000000000000000000000000000000000
--- a/src/core/ext/lb_policy/common.c
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- *
- * Copyright 2016, Google Inc.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- *     * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *     * Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following disclaimer
- * in the documentation and/or other materials provided with the
- * distribution.
- *     * Neither the name of Google Inc. nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- */
-
-#include <grpc/support/log.h>
-
-#include "src/core/ext/lb_policy/common.h"
-
-void add_pollset_or_pollset_set_alternative(
-    grpc_exec_ctx *exec_ctx, grpc_pollset_set *interested_parties,
-    grpc_pollset *pollset, grpc_pollset_set *pollset_set_alternative) {
-  if (pollset != NULL) {
-    GPR_ASSERT(pollset_set_alternative == NULL);
-    grpc_pollset_set_add_pollset(exec_ctx, interested_parties, pollset);
-  } else {
-    GPR_ASSERT(pollset_set_alternative != NULL);
-    grpc_pollset_set_add_pollset_set(exec_ctx, interested_parties,
-                                     pollset_set_alternative);
-  }
-}
-
-void del_pollset_or_pollset_set_alternative(
-    grpc_exec_ctx *exec_ctx, grpc_pollset_set *interested_parties,
-    grpc_pollset *pollset, grpc_pollset_set *pollset_set_alternative) {
-  if (pollset != NULL) {
-    GPR_ASSERT(pollset_set_alternative == NULL);
-    grpc_pollset_set_del_pollset(exec_ctx, interested_parties, pollset);
-  } else {
-    GPR_ASSERT(pollset_set_alternative != NULL);
-    grpc_pollset_set_del_pollset_set(exec_ctx, interested_parties,
-                                     pollset_set_alternative);
-  }
-}
diff --git a/src/core/ext/lb_policy/pick_first/pick_first.c b/src/core/ext/lb_policy/pick_first/pick_first.c
index f6613298be802cff4322b7a3749f21d6e0d751c6..406525d82961d2fe7d35b99c6408581be5ca874d 100644
--- a/src/core/ext/lb_policy/pick_first/pick_first.c
+++ b/src/core/ext/lb_policy/pick_first/pick_first.c
@@ -35,13 +35,11 @@
 
 #include <grpc/support/alloc.h>
 #include "src/core/ext/client_config/lb_policy_registry.h"
-#include "src/core/ext/lb_policy/common.h"
 #include "src/core/lib/transport/connectivity_state.h"
 
 typedef struct pending_pick {
   struct pending_pick *next;
-  grpc_pollset *pollset;
-  grpc_pollset_set *pollset_set_alternative;
+  grpc_pops *pops;
   uint32_t initial_metadata_flags;
   grpc_connected_subchannel **target;
   grpc_closure *on_complete;
@@ -120,9 +118,8 @@ static void pf_shutdown(grpc_exec_ctx *exec_ctx, grpc_lb_policy *pol) {
   while (pp != NULL) {
     pending_pick *next = pp->next;
     *pp->target = NULL;
-    del_pollset_or_pollset_set_alternative(exec_ctx, p->base.interested_parties,
-                                           pp->pollset,
-                                           pp->pollset_set_alternative);
+    grpc_pops_del_to_pollset_set(exec_ctx, pp->pops,
+                                 p->base.interested_parties);
     grpc_exec_ctx_enqueue(exec_ctx, pp->on_complete, true, NULL);
     gpr_free(pp);
     pp = next;
@@ -139,9 +136,8 @@ static void pf_cancel_pick(grpc_exec_ctx *exec_ctx, grpc_lb_policy *pol,
   while (pp != NULL) {
     pending_pick *next = pp->next;
     if (pp->target == target) {
-      del_pollset_or_pollset_set_alternative(
-          exec_ctx, p->base.interested_parties, pp->pollset,
-          pp->pollset_set_alternative);
+      grpc_pops_del_to_pollset_set(exec_ctx, pp->pops,
+                                   p->base.interested_parties);
       *target = NULL;
       grpc_exec_ctx_enqueue(exec_ctx, pp->on_complete, false, NULL);
       gpr_free(pp);
@@ -166,9 +162,8 @@ static void pf_cancel_picks(grpc_exec_ctx *exec_ctx, grpc_lb_policy *pol,
     pending_pick *next = pp->next;
     if ((pp->initial_metadata_flags & initial_metadata_flags_mask) ==
         initial_metadata_flags_eq) {
-      del_pollset_or_pollset_set_alternative(
-          exec_ctx, p->base.interested_parties, pp->pollset,
-          pp->pollset_set_alternative);
+      grpc_pops_del_to_pollset_set(exec_ctx, pp->pops,
+                                   p->base.interested_parties);
       grpc_exec_ctx_enqueue(exec_ctx, pp->on_complete, false, NULL);
       gpr_free(pp);
     } else {
@@ -201,9 +196,7 @@ static void pf_exit_idle(grpc_exec_ctx *exec_ctx, grpc_lb_policy *pol) {
 }
 
 static int pf_pick(grpc_exec_ctx *exec_ctx, grpc_lb_policy *pol,
-                   grpc_pollset *pollset,
-                   grpc_pollset_set *pollset_set_alternative,
-                   grpc_metadata_batch *initial_metadata,
+                   grpc_pops *pops, grpc_metadata_batch *initial_metadata,
                    uint32_t initial_metadata_flags,
                    grpc_connected_subchannel **target,
                    grpc_closure *on_complete) {
@@ -228,12 +221,10 @@ static int pf_pick(grpc_exec_ctx *exec_ctx, grpc_lb_policy *pol,
     if (!p->started_picking) {
       start_picking(exec_ctx, p);
     }
-    add_pollset_or_pollset_set_alternative(exec_ctx, p->base.interested_parties,
-                                           pollset, pollset_set_alternative);
+    grpc_pops_add_to_pollset_set(exec_ctx, pops, p->base.interested_parties);
     pp = gpr_malloc(sizeof(*pp));
     pp->next = p->pending_picks;
-    pp->pollset = pollset;
-    pp->pollset_set_alternative = pollset_set_alternative;
+    pp->pops = pops;
     pp->target = target;
     pp->initial_metadata_flags = initial_metadata_flags;
     pp->on_complete = on_complete;
@@ -313,9 +304,8 @@ static void pf_connectivity_changed(grpc_exec_ctx *exec_ctx, void *arg,
         while ((pp = p->pending_picks)) {
           p->pending_picks = pp->next;
           *pp->target = selected;
-          del_pollset_or_pollset_set_alternative(
-              exec_ctx, p->base.interested_parties, pp->pollset,
-              pp->pollset_set_alternative);
+          grpc_pops_del_to_pollset_set(exec_ctx, pp->pops,
+                                       p->base.interested_parties);
           grpc_exec_ctx_enqueue(exec_ctx, pp->on_complete, true, NULL);
           gpr_free(pp);
         }
diff --git a/src/core/ext/lb_policy/round_robin/round_robin.c b/src/core/ext/lb_policy/round_robin/round_robin.c
index f4e3d60a839cdfa37992a9e62e0c9aa5ddd4353e..69f75b86a3437e83f0925b75c287f3908ff82001 100644
--- a/src/core/ext/lb_policy/round_robin/round_robin.c
+++ b/src/core/ext/lb_policy/round_robin/round_robin.c
@@ -36,7 +36,6 @@
 #include <grpc/support/alloc.h>
 
 #include "src/core/ext/client_config/lb_policy_registry.h"
-#include "src/core/ext/lb_policy/common.h"
 #include "src/core/lib/debug/trace.h"
 #include "src/core/lib/transport/connectivity_state.h"
 
@@ -49,8 +48,7 @@ int grpc_lb_round_robin_trace = 0;
  * Once a pick is available, \a target is updated and \a on_complete called. */
 typedef struct pending_pick {
   struct pending_pick *next;
-  grpc_pollset *pollset;
-  grpc_pollset_set *pollset_set_alternative;
+  grpc_pops *pops;
   uint32_t initial_metadata_flags;
   grpc_connected_subchannel **target;
   grpc_closure *on_complete;
@@ -264,9 +262,8 @@ static void rr_cancel_pick(grpc_exec_ctx *exec_ctx, grpc_lb_policy *pol,
   while (pp != NULL) {
     pending_pick *next = pp->next;
     if (pp->target == target) {
-      del_pollset_or_pollset_set_alternative(
-          exec_ctx, p->base.interested_parties, pp->pollset,
-          pp->pollset_set_alternative);
+      grpc_pops_del_to_pollset_set(exec_ctx, pp->pops,
+                                   p->base.interested_parties);
       *target = NULL;
       grpc_exec_ctx_enqueue(exec_ctx, pp->on_complete, false, NULL);
       gpr_free(pp);
@@ -291,9 +288,8 @@ static void rr_cancel_picks(grpc_exec_ctx *exec_ctx, grpc_lb_policy *pol,
     pending_pick *next = pp->next;
     if ((pp->initial_metadata_flags & initial_metadata_flags_mask) ==
         initial_metadata_flags_eq) {
-      del_pollset_or_pollset_set_alternative(
-          exec_ctx, p->base.interested_parties, pp->pollset,
-          pp->pollset_set_alternative);
+      grpc_pops_del_to_pollset_set(exec_ctx, pp->pops,
+                                   p->base.interested_parties);
       *pp->target = NULL;
       grpc_exec_ctx_enqueue(exec_ctx, pp->on_complete, false, NULL);
       gpr_free(pp);
@@ -333,9 +329,7 @@ static void rr_exit_idle(grpc_exec_ctx *exec_ctx, grpc_lb_policy *pol) {
 }
 
 static int rr_pick(grpc_exec_ctx *exec_ctx, grpc_lb_policy *pol,
-                   grpc_pollset *pollset,
-                   grpc_pollset_set *pollset_set_alternative,
-                   grpc_metadata_batch *initial_metadata,
+                   grpc_pops *pops, grpc_metadata_batch *initial_metadata,
                    uint32_t initial_metadata_flags,
                    grpc_connected_subchannel **target,
                    grpc_closure *on_complete) {
@@ -358,12 +352,10 @@ static int rr_pick(grpc_exec_ctx *exec_ctx, grpc_lb_policy *pol,
     if (!p->started_picking) {
       start_picking(exec_ctx, p);
     }
-    add_pollset_or_pollset_set_alternative(exec_ctx, p->base.interested_parties,
-                                           pollset, pollset_set_alternative);
+    grpc_pops_add_to_pollset_set(exec_ctx, pops, p->base.interested_parties);
     pp = gpr_malloc(sizeof(*pp));
     pp->next = p->pending_picks;
-    pp->pollset = pollset;
-    pp->pollset_set_alternative = pollset_set_alternative;
+    pp->pops = pops;
     pp->target = target;
     pp->on_complete = on_complete;
     pp->initial_metadata_flags = initial_metadata_flags;
@@ -412,9 +404,8 @@ static void rr_connectivity_changed(grpc_exec_ctx *exec_ctx, void *arg,
                     "[RR CONN CHANGED] TARGET <-- SUBCHANNEL %p (NODE %p)",
                     selected->subchannel, selected);
           }
-          del_pollset_or_pollset_set_alternative(
-              exec_ctx, p->base.interested_parties, pp->pollset,
-              pp->pollset_set_alternative);
+          grpc_pops_del_to_pollset_set(exec_ctx, pp->pops,
+                                       p->base.interested_parties);
           grpc_exec_ctx_enqueue(exec_ctx, pp->on_complete, true, NULL);
           gpr_free(pp);
         }
diff --git a/src/core/lib/channel/channel_stack.c b/src/core/lib/channel/channel_stack.c
index e02c164aaf0f4a838bfb142c62fa6d51f58ac672..cedc8ade82e1362976634324940751f095be0292 100644
--- a/src/core/lib/channel/channel_stack.c
+++ b/src/core/lib/channel/channel_stack.c
@@ -189,32 +189,30 @@ void grpc_call_stack_init(grpc_exec_ctx *exec_ctx,
   }
 }
 
-void grpc_call_stack_set_pollset_or_pollset_set(
-    grpc_exec_ctx *exec_ctx, grpc_call_stack *call_stack, grpc_pollset *pollset,
-    grpc_pollset_set *pollset_set_alternative) {
+void grpc_call_stack_set_pollset_or_pollset_set(grpc_exec_ctx *exec_ctx,
+                                                grpc_call_stack *call_stack,
+                                                grpc_pops *pops) {
   size_t count = call_stack->count;
   grpc_call_element *call_elems;
   char *user_data;
   size_t i;
 
-  GPR_ASSERT((pollset == NULL) + (pollset_set_alternative == NULL) == 1);
-  GPR_ASSERT(pollset != NULL || pollset_set_alternative != NULL);
   call_elems = CALL_ELEMS_FROM_STACK(call_stack);
   user_data = ((char *)call_elems) +
               ROUND_UP_TO_ALIGNMENT_SIZE(count * sizeof(grpc_call_element));
 
   /* init per-filter data */
   for (i = 0; i < count; i++) {
-    call_elems[i].filter->set_pollset_or_pollset_set(
-        exec_ctx, &call_elems[i], pollset, pollset_set_alternative);
+    call_elems[i].filter->set_pollset_or_pollset_set(exec_ctx, &call_elems[i],
+                                                     pops);
     user_data +=
         ROUND_UP_TO_ALIGNMENT_SIZE(call_elems[i].filter->sizeof_call_data);
   }
 }
 
-void grpc_call_stack_ignore_set_pollset_or_pollset_set(
-    grpc_exec_ctx *exec_ctx, grpc_call_element *elem, grpc_pollset *pollset,
-    grpc_pollset_set *pollset_set_alternative) {}
+void grpc_call_stack_ignore_set_pollset_or_pollset_set(grpc_exec_ctx *exec_ctx,
+                                                       grpc_call_element *elem,
+                                                       grpc_pops *pops) {}
 
 void grpc_call_stack_destroy(grpc_exec_ctx *exec_ctx, grpc_call_stack *stack) {
   grpc_call_element *elems = CALL_ELEMS_FROM_STACK(stack);
diff --git a/src/core/lib/channel/channel_stack.h b/src/core/lib/channel/channel_stack.h
index 027bb23c1dd4e74638bc238434f4c0e17a5191ef..c9d13ef193c5c4795b5bfc119b8da91dcbd125f7 100644
--- a/src/core/lib/channel/channel_stack.h
+++ b/src/core/lib/channel/channel_stack.h
@@ -46,6 +46,7 @@
 #include <grpc/grpc.h>
 #include <grpc/support/log.h>
 #include "src/core/lib/debug/trace.h"
+#include "src/core/lib/iomgr/pops.h"
 #include "src/core/lib/transport/transport.h"
 
 typedef struct grpc_channel_element grpc_channel_element;
@@ -102,9 +103,7 @@ typedef struct {
   void (*init_call_elem)(grpc_exec_ctx *exec_ctx, grpc_call_element *elem,
                          grpc_call_element_args *args);
   void (*set_pollset_or_pollset_set)(grpc_exec_ctx *exec_ctx,
-                                     grpc_call_element *elem,
-                                     grpc_pollset *pollset,
-                                     grpc_pollset_set *pollset_set_alternative);
+                                     grpc_call_element *elem, grpc_pops *pops);
   /* Destroy per call data.
      The filter does not need to do any chaining */
   void (*destroy_call_elem)(grpc_exec_ctx *exec_ctx, grpc_call_element *elem);
@@ -201,9 +200,9 @@ void grpc_call_stack_init(grpc_exec_ctx *exec_ctx,
                           grpc_call_stack *call_stack);
 /* Set a pollset or a pollset_set for a call stack: must occur before the first
  * op is started */
-void grpc_call_stack_set_pollset_or_pollset_set(
-    grpc_exec_ctx *exec_ctx, grpc_call_stack *call_stack, grpc_pollset *pollset,
-    grpc_pollset_set *pollset_set_alternative);
+void grpc_call_stack_set_pollset_or_pollset_set(grpc_exec_ctx *exec_ctx,
+                                                grpc_call_stack *call_stack,
+                                                grpc_pops *pops);
 
 #ifdef GRPC_STREAM_REFCOUNT_DEBUG
 #define GRPC_CALL_STACK_REF(call_stack, reason) \
@@ -228,12 +227,11 @@ void grpc_call_stack_set_pollset_or_pollset_set(
 /* Destroy a call stack */
 void grpc_call_stack_destroy(grpc_exec_ctx *exec_ctx, grpc_call_stack *stack);
 
-/* Ignore set pollset{_set} - used by filters to implement the
- * set_pollset_or_pollset_set method if they don't care about pollsets at all.
- * Does nothing. */
-void grpc_call_stack_ignore_set_pollset_or_pollset_set(
-    grpc_exec_ctx *exec_ctx, grpc_call_element *elem, grpc_pollset *pollset,
-    grpc_pollset_set *pollset_set_alternative);
+/* Ignore set pollset{_set} - used by filters if they don't care about pollsets
+ * at all. Does nothing. */
+void grpc_call_stack_ignore_set_pollset_or_pollset_set(grpc_exec_ctx *exec_ctx,
+                                                       grpc_call_element *elem,
+                                                       grpc_pops *pops);
 /* Call the next operation in a call stack */
 void grpc_call_next_op(grpc_exec_ctx *exec_ctx, grpc_call_element *elem,
                        grpc_transport_stream_op *op);
diff --git a/src/core/lib/channel/connected_channel.c b/src/core/lib/channel/connected_channel.c
index 405f0a2ecd6abbcb0fa25d481a3fb6d1778b5102..314ff6e5ce88f44c2a54ebcc2af14c7f276834f5 100644
--- a/src/core/lib/channel/connected_channel.c
+++ b/src/core/lib/channel/connected_channel.c
@@ -93,22 +93,13 @@ static void init_call_elem(grpc_exec_ctx *exec_ctx, grpc_call_element *elem,
   GPR_ASSERT(r == 0);
 }
 
-static void set_pollset_or_pollset_set(
-    grpc_exec_ctx *exec_ctx, grpc_call_element *elem, grpc_pollset *pollset,
-    grpc_pollset_set *pollset_set_alternative) {
-  GPR_ASSERT((pollset == NULL) + (pollset_set_alternative == NULL) == 1);
-  GPR_ASSERT(pollset != NULL || pollset_set_alternative != NULL);
-
+static void set_pollset_or_pollset_set(grpc_exec_ctx *exec_ctx,
+                                       grpc_call_element *elem,
+                                       grpc_pops *pops) {
   call_data *calld = elem->call_data;
   channel_data *chand = elem->channel_data;
-  if (pollset != NULL) {
-    grpc_transport_set_pollset(exec_ctx, chand->transport,
-                               TRANSPORT_STREAM_FROM_CALL_DATA(calld), pollset);
-  } else if (pollset_set_alternative != NULL) {
-    grpc_transport_set_pollset_set(exec_ctx, chand->transport,
-                                   TRANSPORT_STREAM_FROM_CALL_DATA(calld),
-                                   pollset_set_alternative);
-  }
+  grpc_transport_set_pops(exec_ctx, chand->transport,
+                          TRANSPORT_STREAM_FROM_CALL_DATA(calld), pops);
 }
 
 /* Destructor for call_data */
diff --git a/src/core/lib/http/httpcli.c b/src/core/lib/http/httpcli.c
index 4cf335868028c3135b729eec6cd6750f43f5c4e6..d48e954479600da19d812a88865b1b20914543f9 100644
--- a/src/core/lib/http/httpcli.c
+++ b/src/core/lib/http/httpcli.c
@@ -62,7 +62,7 @@ typedef struct {
   grpc_httpcli_response_cb on_response;
   void *user_data;
   grpc_httpcli_context *context;
-  grpc_pollset_set *pollset_set;
+  grpc_pops *pops;
   grpc_iomgr_object iomgr_obj;
   gpr_slice_buffer incoming;
   gpr_slice_buffer outgoing;
@@ -97,8 +97,7 @@ static void next_address(grpc_exec_ctx *exec_ctx, internal_request *req);
 
 static void finish(grpc_exec_ctx *exec_ctx, internal_request *req,
                    int success) {
-  grpc_pollset_set_del_pollset_set(exec_ctx, req->context->pollset_set,
-                                   req->pollset_set);
+  grpc_pops_del_to_pollset_set(exec_ctx, req->pops, req->context->pollset_set);
   req->on_response(exec_ctx, req->user_data,
                    success ? &req->parser.http.response : NULL);
   grpc_http_parser_destroy(&req->parser);
@@ -221,10 +220,10 @@ static void on_resolved(grpc_exec_ctx *exec_ctx, void *arg,
 }
 
 static void internal_request_begin(
-    grpc_exec_ctx *exec_ctx, grpc_httpcli_context *context,
-    grpc_pollset_set *pollset_set, const grpc_httpcli_request *request,
-    gpr_timespec deadline, grpc_httpcli_response_cb on_response,
-    void *user_data, const char *name, gpr_slice request_text) {
+    grpc_exec_ctx *exec_ctx, grpc_httpcli_context *context, grpc_pops *pops,
+    const grpc_httpcli_request *request, gpr_timespec deadline,
+    grpc_httpcli_response_cb on_response, void *user_data, const char *name,
+    gpr_slice request_text) {
   internal_request *req = gpr_malloc(sizeof(internal_request));
   memset(req, 0, sizeof(*req));
   req->request_text = request_text;
@@ -235,7 +234,7 @@ static void internal_request_begin(
   req->handshaker =
       request->handshaker ? request->handshaker : &grpc_httpcli_plaintext;
   req->context = context;
-  req->pollset_set = pollset_set;
+  req->pops = pops;
   grpc_closure_init(&req->on_read, on_read, req);
   grpc_closure_init(&req->done_write, done_write, req);
   gpr_slice_buffer_init(&req->incoming);
@@ -244,15 +243,14 @@ static void internal_request_begin(
   req->host = gpr_strdup(request->host);
   req->ssl_host_override = gpr_strdup(request->ssl_host_override);
 
-  grpc_pollset_set_add_pollset_set(exec_ctx, req->context->pollset_set,
-                                   req->pollset_set);
+  GPR_ASSERT(pops);
+  grpc_pops_add_to_pollset_set(exec_ctx, req->pops, req->context->pollset_set);
   grpc_resolve_address(exec_ctx, request->host, req->handshaker->default_port,
                        on_resolved, req);
 }
 
 void grpc_httpcli_get(grpc_exec_ctx *exec_ctx, grpc_httpcli_context *context,
-                      grpc_pollset_set *pollset_set,
-                      const grpc_httpcli_request *request,
+                      grpc_pops *pops, const grpc_httpcli_request *request,
                       gpr_timespec deadline,
                       grpc_httpcli_response_cb on_response, void *user_data) {
   char *name;
@@ -261,15 +259,14 @@ void grpc_httpcli_get(grpc_exec_ctx *exec_ctx, grpc_httpcli_context *context,
     return;
   }
   gpr_asprintf(&name, "HTTP:GET:%s:%s", request->host, request->http.path);
-  internal_request_begin(exec_ctx, context, pollset_set, request, deadline,
+  internal_request_begin(exec_ctx, context, pops, request, deadline,
                          on_response, user_data, name,
                          grpc_httpcli_format_get_request(request));
   gpr_free(name);
 }
 
 void grpc_httpcli_post(grpc_exec_ctx *exec_ctx, grpc_httpcli_context *context,
-                       grpc_pollset_set *pollset_set,
-                       const grpc_httpcli_request *request,
+                       grpc_pops *pops, const grpc_httpcli_request *request,
                        const char *body_bytes, size_t body_size,
                        gpr_timespec deadline,
                        grpc_httpcli_response_cb on_response, void *user_data) {
@@ -281,8 +278,8 @@ void grpc_httpcli_post(grpc_exec_ctx *exec_ctx, grpc_httpcli_context *context,
   }
   gpr_asprintf(&name, "HTTP:POST:%s:%s", request->host, request->http.path);
   internal_request_begin(
-      exec_ctx, context, pollset_set, request, deadline, on_response, user_data,
-      name, grpc_httpcli_format_post_request(request, body_bytes, body_size));
+      exec_ctx, context, pops, request, deadline, on_response, user_data, name,
+      grpc_httpcli_format_post_request(request, body_bytes, body_size));
   gpr_free(name);
 }
 
diff --git a/src/core/lib/http/httpcli.h b/src/core/lib/http/httpcli.h
index 040b5f476132127767ecee414eb42153ee010b08..1166cfeea74b90cb2819a4806f10857ae43e7a60 100644
--- a/src/core/lib/http/httpcli.h
+++ b/src/core/lib/http/httpcli.h
@@ -42,6 +42,7 @@
 #include "src/core/lib/iomgr/endpoint.h"
 #include "src/core/lib/iomgr/iomgr_internal.h"
 #include "src/core/lib/iomgr/pollset_set.h"
+#include "src/core/lib/iomgr/pops.h"
 
 /* User agent this library reports */
 #define GRPC_HTTPCLI_USER_AGENT "grpc-httpcli/0.0"
@@ -100,8 +101,7 @@ void grpc_httpcli_context_destroy(grpc_httpcli_context *context);
    'on_response' is a callback to report results to (and 'user_data' is a user
      supplied pointer to pass to said call) */
 void grpc_httpcli_get(grpc_exec_ctx *exec_ctx, grpc_httpcli_context *context,
-                      grpc_pollset_set *pollset_set,
-                      const grpc_httpcli_request *request,
+                      grpc_pops *pops, const grpc_httpcli_request *request,
                       gpr_timespec deadline,
                       grpc_httpcli_response_cb on_response, void *user_data);
 
@@ -121,8 +121,7 @@ void grpc_httpcli_get(grpc_exec_ctx *exec_ctx, grpc_httpcli_context *context,
      supplied pointer to pass to said call)
    Does not support ?var1=val1&var2=val2 in the path. */
 void grpc_httpcli_post(grpc_exec_ctx *exec_ctx, grpc_httpcli_context *context,
-                       grpc_pollset_set *pollset_set,
-                       const grpc_httpcli_request *request,
+                       grpc_pops *pops, const grpc_httpcli_request *request,
                        const char *body_bytes, size_t body_size,
                        gpr_timespec deadline,
                        grpc_httpcli_response_cb on_response, void *user_data);
diff --git a/src/core/lib/iomgr/pops.c b/src/core/lib/iomgr/pops.c
new file mode 100644
index 0000000000000000000000000000000000000000..c2629f20aa67e95b6f54379d2a2ee79c5d7cba4e
--- /dev/null
+++ b/src/core/lib/iomgr/pops.c
@@ -0,0 +1,103 @@
+/*
+ *
+ * Copyright 2016, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+
+#include "src/core/lib/iomgr/pops.h"
+
+struct grpc_pops {
+  union {
+    grpc_pollset *pollset;
+    grpc_pollset_set *pollset_set;
+  } pops;
+  enum pops_tag { POLLSET, POLLSET_SET } tag;
+};
+
+grpc_pops *grpc_pops_create_from_pollset_set(grpc_pollset_set *pollset_set) {
+  grpc_pops *pops = gpr_malloc(sizeof(grpc_pops));
+  pops->pops.pollset_set = pollset_set;
+  pops->tag = POLLSET_SET;
+  return pops;
+}
+
+grpc_pops *grpc_pops_create_from_pollset(grpc_pollset *pollset) {
+  grpc_pops *pops = gpr_malloc(sizeof(grpc_pops));
+  pops->pops.pollset = pollset;
+  pops->tag = POLLSET;
+  return pops;
+}
+
+void grpc_pops_destroy(grpc_pops *pops) { gpr_free(pops); }
+
+grpc_pollset *grpc_pops_pollset(grpc_pops *pops) {
+  if (pops->tag == POLLSET) {
+    return pops->pops.pollset;
+  }
+  return NULL;
+}
+
+grpc_pollset_set *grpc_pops_pollset_set(grpc_pops *pops) {
+  if (pops->tag == POLLSET_SET) {
+    return pops->pops.pollset_set;
+  }
+  return NULL;
+}
+
+void grpc_pops_add_to_pollset_set(grpc_exec_ctx *exec_ctx, grpc_pops *pops,
+                                  grpc_pollset_set *pss_dst) {
+  if (pops->tag == POLLSET) {
+    GPR_ASSERT(pops->pops.pollset != NULL);
+    grpc_pollset_set_add_pollset(exec_ctx, pss_dst, pops->pops.pollset);
+  } else if (pops->tag == POLLSET_SET) {
+    GPR_ASSERT(pops->pops.pollset_set != NULL);
+    grpc_pollset_set_add_pollset_set(exec_ctx, pss_dst, pops->pops.pollset_set);
+  } else {
+    gpr_log(GPR_ERROR, "Invalid grpc_pops tag '%d'", pops->tag);
+    abort();
+  }
+}
+
+void grpc_pops_del_to_pollset_set(grpc_exec_ctx *exec_ctx, grpc_pops *pops,
+                                  grpc_pollset_set *pss_dst) {
+  if (pops->tag == POLLSET) {
+    GPR_ASSERT(pops->pops.pollset != NULL);
+    grpc_pollset_set_del_pollset(exec_ctx, pss_dst, pops->pops.pollset);
+  } else if (pops->tag == POLLSET_SET) {
+    GPR_ASSERT(pops->pops.pollset_set != NULL);
+    grpc_pollset_set_del_pollset_set(exec_ctx, pss_dst, pops->pops.pollset_set);
+  } else {
+    gpr_log(GPR_ERROR, "Invalid grpc_pops tag '%d'", pops->tag);
+    abort();
+  }
+}
diff --git a/src/core/ext/lb_policy/common.h b/src/core/lib/iomgr/pops.h
similarity index 55%
rename from src/core/ext/lb_policy/common.h
rename to src/core/lib/iomgr/pops.h
index 1ace967328da6eedfb54f942818559cc7c491b78..24ff85748bcdaa582f5c6901d4dcc6d09e15d4ae 100644
--- a/src/core/ext/lb_policy/common.h
+++ b/src/core/lib/iomgr/pops.h
@@ -1,6 +1,6 @@
 /*
  *
- * Copyright 2016, Google Inc.
+ * Copyright 2015, Google Inc.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -31,18 +31,39 @@
  *
  */
 
-#ifndef GRPC_CORE_EXT_LB_POLICY_COMMON_H
-#define GRPC_CORE_EXT_LB_POLICY_COMMON_H
+#ifndef GRPC_CORE_LIB_IOMGR_POPS_H
+#define GRPC_CORE_LIB_IOMGR_POPS_H
 
 #include "src/core/lib/iomgr/pollset.h"
 #include "src/core/lib/iomgr/pollset_set.h"
 
-void add_pollset_or_pollset_set_alternative(
-    grpc_exec_ctx *exec_ctx, grpc_pollset_set *interested_parties,
-    grpc_pollset *pollset, grpc_pollset_set *pollset_set_alternative);
+/* A grpc_pops is a pollset-or-pollset_set container. It allows functions that
+ * accept a pollset XOR a pollset_set to do so through an abstract interface.
+ * No ownership is taken. */
 
-void del_pollset_or_pollset_set_alternative(
-    grpc_exec_ctx *exec_ctx, grpc_pollset_set *interested_parties,
-    grpc_pollset *pollset, grpc_pollset_set *pollset_set_alternative);
+typedef struct grpc_pops grpc_pops;
 
-#endif /* GRPC_CORE_EXT_LB_POLICY_COMMON_H */
+grpc_pops *grpc_pops_create_from_pollset_set(grpc_pollset_set *pollset_set);
+grpc_pops *grpc_pops_create_from_pollset(grpc_pollset *pollset);
+
+/** If \a pops contains a pollset, return it. Otherwise, return NULL */
+grpc_pollset *grpc_pops_pollset(grpc_pops *pops);
+
+/** If \a pops contains a pollset_set, return it. Otherwise, return NULL */
+grpc_pollset_set *grpc_pops_pollset_set(grpc_pops *pops);
+
+void grpc_pops_destroy(grpc_pops *pops);
+
+/** Add the pollset or pollset_set in \a pops to the destination pollset_set \a
+ * pss_dst */
+void grpc_pops_add_to_pollset_set(grpc_exec_ctx *exec_ctx, grpc_pops *pops,
+                                  grpc_pollset_set *pss_dst);
+
+/** Delete the pollset or pollset_set in \a pops from the destination
+ * pollset_set \a
+ * pss_dst */
+void grpc_pops_del_to_pollset_set(grpc_exec_ctx *exec_ctx, grpc_pops *pops,
+                                  grpc_pollset_set *pss_dst);
+/* pollset_set specific */
+
+#endif /* GRPC_CORE_LIB_IOMGR_POPS_H */
diff --git a/src/core/lib/security/client_auth_filter.c b/src/core/lib/security/client_auth_filter.c
index 5db9755c2be8e7d190563dadeb0a516f73f1610f..1511659dfef20888514a0c4182860ac2aad35481 100644
--- a/src/core/lib/security/client_auth_filter.c
+++ b/src/core/lib/security/client_auth_filter.c
@@ -54,11 +54,11 @@ typedef struct {
   grpc_call_credentials *creds;
   grpc_mdstr *host;
   grpc_mdstr *method;
-  /* pollset_set bound to this call; if we need to make external
+  /* pollset{_set} bound to this call; if we need to make external
      network requests, they should be done under a pollset added to this
      pollset_set so that work can progress when this call wants work to progress
   */
-  grpc_pollset_set *pollset_set;
+  grpc_pops *pops;
   grpc_transport_stream_op op;
   uint8_t security_context_set;
   grpc_linked_mdelem md_links[MAX_CREDENTIALS_METADATA_COUNT];
@@ -184,9 +184,9 @@ static void send_security_metadata(grpc_exec_ctx *exec_ctx,
   build_auth_metadata_context(&chand->security_connector->base,
                               chand->auth_context, calld);
   calld->op = *op; /* Copy op (originates from the caller's stack). */
-  GPR_ASSERT(calld->pollset_set);
+  GPR_ASSERT(calld->pops != NULL);
   grpc_call_credentials_get_request_metadata(
-      exec_ctx, calld->creds, calld->pollset_set, calld->auth_md_context,
+      exec_ctx, calld->creds, calld->pops, calld->auth_md_context,
       on_credentials_metadata, elem);
 }
 
@@ -268,22 +268,13 @@ static void init_call_elem(grpc_exec_ctx *exec_ctx, grpc_call_element *elem,
                            grpc_call_element_args *args) {
   call_data *calld = elem->call_data;
   memset(calld, 0, sizeof(*calld));
-  calld->pollset_set = grpc_pollset_set_create();
 }
 
-static void set_pollset_or_pollset_set(
-    grpc_exec_ctx *exec_ctx, grpc_call_element *elem, grpc_pollset *pollset,
-    grpc_pollset_set *pollset_set_alternative) {
-  GPR_ASSERT((pollset == NULL) + (pollset_set_alternative == NULL) == 1);
-  GPR_ASSERT(pollset != NULL || pollset_set_alternative != NULL);
-
+static void set_pollset_or_pollset_set(grpc_exec_ctx *exec_ctx,
+                                       grpc_call_element *elem,
+                                       grpc_pops *pops) {
   call_data *calld = elem->call_data;
-  if (pollset != NULL) {
-    grpc_pollset_set_add_pollset(exec_ctx, calld->pollset_set, pollset);
-  } else if (pollset_set_alternative != NULL) {
-    grpc_pollset_set_add_pollset_set(exec_ctx, calld->pollset_set,
-                                     pollset_set_alternative);
-  }
+  calld->pops = pops;
 }
 
 /* Destructor for call_data */
@@ -298,7 +289,6 @@ static void destroy_call_elem(grpc_exec_ctx *exec_ctx,
     GRPC_MDSTR_UNREF(calld->method);
   }
   reset_auth_metadata_context(&calld->auth_md_context);
-  grpc_pollset_set_destroy(calld->pollset_set);
 }
 
 /* Constructor for channel_data */
diff --git a/src/core/lib/security/credentials.c b/src/core/lib/security/credentials.c
index 8550649cbf3dd789708c87359422e2df73bc52d2..43c061a265a0fa626d6926489430b6e8543e3c9d 100644
--- a/src/core/lib/security/credentials.c
+++ b/src/core/lib/security/credentials.c
@@ -117,16 +117,16 @@ void grpc_call_credentials_release(grpc_call_credentials *creds) {
 }
 
 void grpc_call_credentials_get_request_metadata(
-    grpc_exec_ctx *exec_ctx, grpc_call_credentials *creds,
-    grpc_pollset_set *pollset_set, grpc_auth_metadata_context context,
-    grpc_credentials_metadata_cb cb, void *user_data) {
+    grpc_exec_ctx *exec_ctx, grpc_call_credentials *creds, grpc_pops *pops,
+    grpc_auth_metadata_context context, grpc_credentials_metadata_cb cb,
+    void *user_data) {
   if (creds == NULL || creds->vtable->get_request_metadata == NULL) {
     if (cb != NULL) {
       cb(exec_ctx, user_data, NULL, 0, GRPC_CREDENTIALS_OK);
     }
     return;
   }
-  creds->vtable->get_request_metadata(exec_ctx, creds, pollset_set, context, cb,
+  creds->vtable->get_request_metadata(exec_ctx, creds, pops, context, cb,
                                       user_data);
 }
 
@@ -448,7 +448,7 @@ static void jwt_destruct(grpc_call_credentials *creds) {
 
 static void jwt_get_request_metadata(grpc_exec_ctx *exec_ctx,
                                      grpc_call_credentials *creds,
-                                     grpc_pollset_set *pollset_set,
+                                     grpc_pops *pops,
                                      grpc_auth_metadata_context context,
                                      grpc_credentials_metadata_cb cb,
                                      void *user_data) {
@@ -670,9 +670,9 @@ static void on_oauth2_token_fetcher_http_response(
 }
 
 static void oauth2_token_fetcher_get_request_metadata(
-    grpc_exec_ctx *exec_ctx, grpc_call_credentials *creds,
-    grpc_pollset_set *pollset_set, grpc_auth_metadata_context context,
-    grpc_credentials_metadata_cb cb, void *user_data) {
+    grpc_exec_ctx *exec_ctx, grpc_call_credentials *creds, grpc_pops *pops,
+    grpc_auth_metadata_context context, grpc_credentials_metadata_cb cb,
+    void *user_data) {
   grpc_oauth2_token_fetcher_credentials *c =
       (grpc_oauth2_token_fetcher_credentials *)creds;
   gpr_timespec refresh_threshold = gpr_time_from_seconds(
@@ -697,7 +697,7 @@ static void oauth2_token_fetcher_get_request_metadata(
     c->fetch_func(
         exec_ctx,
         grpc_credentials_metadata_request_create(creds, cb, user_data),
-        &c->httpcli_context, pollset_set, on_oauth2_token_fetcher_http_response,
+        &c->httpcli_context, pops, on_oauth2_token_fetcher_http_response,
         gpr_time_add(gpr_now(GPR_CLOCK_REALTIME), refresh_threshold));
   }
 }
@@ -720,7 +720,7 @@ static grpc_call_credentials_vtable compute_engine_vtable = {
 
 static void compute_engine_fetch_oauth2(
     grpc_exec_ctx *exec_ctx, grpc_credentials_metadata_request *metadata_req,
-    grpc_httpcli_context *httpcli_context, grpc_pollset_set *pollset_set,
+    grpc_httpcli_context *httpcli_context, grpc_pops *pops,
     grpc_httpcli_response_cb response_cb, gpr_timespec deadline) {
   grpc_http_header header = {"Metadata-Flavor", "Google"};
   grpc_httpcli_request request;
@@ -729,7 +729,7 @@ static void compute_engine_fetch_oauth2(
   request.http.path = GRPC_COMPUTE_ENGINE_METADATA_TOKEN_PATH;
   request.http.hdr_count = 1;
   request.http.hdrs = &header;
-  grpc_httpcli_get(exec_ctx, httpcli_context, pollset_set, &request, deadline,
+  grpc_httpcli_get(exec_ctx, httpcli_context, pops, &request, deadline,
                    response_cb, metadata_req);
 }
 
@@ -759,7 +759,7 @@ static grpc_call_credentials_vtable refresh_token_vtable = {
 
 static void refresh_token_fetch_oauth2(
     grpc_exec_ctx *exec_ctx, grpc_credentials_metadata_request *metadata_req,
-    grpc_httpcli_context *httpcli_context, grpc_pollset_set *pollset_set,
+    grpc_httpcli_context *httpcli_context, grpc_pops *pops,
     grpc_httpcli_response_cb response_cb, gpr_timespec deadline) {
   grpc_google_refresh_token_credentials *c =
       (grpc_google_refresh_token_credentials *)metadata_req->creds;
@@ -776,7 +776,7 @@ static void refresh_token_fetch_oauth2(
   request.http.hdr_count = 1;
   request.http.hdrs = &header;
   request.handshaker = &grpc_httpcli_ssl;
-  grpc_httpcli_post(exec_ctx, httpcli_context, pollset_set, &request, body,
+  grpc_httpcli_post(exec_ctx, httpcli_context, pops, &request, body,
                     strlen(body), deadline, response_cb, metadata_req);
   gpr_free(body);
 }
@@ -826,9 +826,9 @@ static void on_simulated_token_fetch_done(grpc_exec_ctx *exec_ctx,
 }
 
 static void md_only_test_get_request_metadata(
-    grpc_exec_ctx *exec_ctx, grpc_call_credentials *creds,
-    grpc_pollset_set *pollset_set, grpc_auth_metadata_context context,
-    grpc_credentials_metadata_cb cb, void *user_data) {
+    grpc_exec_ctx *exec_ctx, grpc_call_credentials *creds, grpc_pops *pops,
+    grpc_auth_metadata_context context, grpc_credentials_metadata_cb cb,
+    void *user_data) {
   grpc_md_only_test_credentials *c = (grpc_md_only_test_credentials *)creds;
 
   if (c->is_async) {
@@ -866,9 +866,9 @@ static void access_token_destruct(grpc_call_credentials *creds) {
 }
 
 static void access_token_get_request_metadata(
-    grpc_exec_ctx *exec_ctx, grpc_call_credentials *creds,
-    grpc_pollset_set *pollset_set, grpc_auth_metadata_context context,
-    grpc_credentials_metadata_cb cb, void *user_data) {
+    grpc_exec_ctx *exec_ctx, grpc_call_credentials *creds, grpc_pops *pops,
+    grpc_auth_metadata_context context, grpc_credentials_metadata_cb cb,
+    void *user_data) {
   grpc_access_token_credentials *c = (grpc_access_token_credentials *)creds;
   cb(exec_ctx, user_data, c->access_token_md->entries, 1, GRPC_CREDENTIALS_OK);
 }
@@ -951,7 +951,7 @@ typedef struct {
   grpc_credentials_md_store *md_elems;
   grpc_auth_metadata_context auth_md_context;
   void *user_data;
-  grpc_pollset_set *pollset_set;
+  grpc_pops *pops;
   grpc_credentials_metadata_cb cb;
 } grpc_composite_call_credentials_metadata_context;
 
@@ -994,9 +994,9 @@ static void composite_call_metadata_cb(grpc_exec_ctx *exec_ctx, void *user_data,
   if (ctx->creds_index < ctx->composite_creds->inner.num_creds) {
     grpc_call_credentials *inner_creds =
         ctx->composite_creds->inner.creds_array[ctx->creds_index++];
-    grpc_call_credentials_get_request_metadata(
-        exec_ctx, inner_creds, ctx->pollset_set, ctx->auth_md_context,
-        composite_call_metadata_cb, ctx);
+    grpc_call_credentials_get_request_metadata(exec_ctx, inner_creds, ctx->pops,
+                                               ctx->auth_md_context,
+                                               composite_call_metadata_cb, ctx);
     return;
   }
 
@@ -1007,9 +1007,9 @@ static void composite_call_metadata_cb(grpc_exec_ctx *exec_ctx, void *user_data,
 }
 
 static void composite_call_get_request_metadata(
-    grpc_exec_ctx *exec_ctx, grpc_call_credentials *creds,
-    grpc_pollset_set *pollset_set, grpc_auth_metadata_context auth_md_context,
-    grpc_credentials_metadata_cb cb, void *user_data) {
+    grpc_exec_ctx *exec_ctx, grpc_call_credentials *creds, grpc_pops *pops,
+    grpc_auth_metadata_context auth_md_context, grpc_credentials_metadata_cb cb,
+    void *user_data) {
   grpc_composite_call_credentials *c = (grpc_composite_call_credentials *)creds;
   grpc_composite_call_credentials_metadata_context *ctx;
 
@@ -1019,11 +1019,11 @@ static void composite_call_get_request_metadata(
   ctx->user_data = user_data;
   ctx->cb = cb;
   ctx->composite_creds = c;
-  ctx->pollset_set = pollset_set;
+  ctx->pops = pops;
   ctx->md_elems = grpc_credentials_md_store_create(c->inner.num_creds);
   grpc_call_credentials_get_request_metadata(
-      exec_ctx, c->inner.creds_array[ctx->creds_index++], pollset_set,
-      auth_md_context, composite_call_metadata_cb, ctx);
+      exec_ctx, c->inner.creds_array[ctx->creds_index++], pops, auth_md_context,
+      composite_call_metadata_cb, ctx);
 }
 
 static grpc_call_credentials_vtable composite_call_credentials_vtable = {
@@ -1116,7 +1116,7 @@ static void iam_destruct(grpc_call_credentials *creds) {
 
 static void iam_get_request_metadata(grpc_exec_ctx *exec_ctx,
                                      grpc_call_credentials *creds,
-                                     grpc_pollset_set *pollset_set,
+                                     grpc_pops *pops,
                                      grpc_auth_metadata_context context,
                                      grpc_credentials_metadata_cb cb,
                                      void *user_data) {
@@ -1205,7 +1205,7 @@ static void plugin_md_request_metadata_ready(void *request,
 
 static void plugin_get_request_metadata(grpc_exec_ctx *exec_ctx,
                                         grpc_call_credentials *creds,
-                                        grpc_pollset_set *pollset_set,
+                                        grpc_pops *pops,
                                         grpc_auth_metadata_context context,
                                         grpc_credentials_metadata_cb cb,
                                         void *user_data) {
diff --git a/src/core/lib/security/credentials.h b/src/core/lib/security/credentials.h
index 9512e8e028e033fd0ed2c4154949c84cad5fe761..3d374949d5912d901c2317c6338b140c932135c6 100644
--- a/src/core/lib/security/credentials.h
+++ b/src/core/lib/security/credentials.h
@@ -41,6 +41,7 @@
 
 #include "src/core/lib/http/httpcli.h"
 #include "src/core/lib/http/parser.h"
+#include "src/core/lib/iomgr/pops.h"
 #include "src/core/lib/security/json_token.h"
 #include "src/core/lib/security/security_connector.h"
 
@@ -169,8 +170,7 @@ typedef void (*grpc_credentials_metadata_cb)(grpc_exec_ctx *exec_ctx,
 typedef struct {
   void (*destruct)(grpc_call_credentials *c);
   void (*get_request_metadata)(grpc_exec_ctx *exec_ctx,
-                               grpc_call_credentials *c,
-                               grpc_pollset_set *pollset_set,
+                               grpc_call_credentials *c, grpc_pops *pops,
                                grpc_auth_metadata_context context,
                                grpc_credentials_metadata_cb cb,
                                void *user_data);
@@ -185,9 +185,9 @@ struct grpc_call_credentials {
 grpc_call_credentials *grpc_call_credentials_ref(grpc_call_credentials *creds);
 void grpc_call_credentials_unref(grpc_call_credentials *creds);
 void grpc_call_credentials_get_request_metadata(
-    grpc_exec_ctx *exec_ctx, grpc_call_credentials *creds,
-    grpc_pollset_set *pollset_set, grpc_auth_metadata_context context,
-    grpc_credentials_metadata_cb cb, void *user_data);
+    grpc_exec_ctx *exec_ctx, grpc_call_credentials *creds, grpc_pops *pops,
+    grpc_auth_metadata_context context, grpc_credentials_metadata_cb cb,
+    void *user_data);
 
 typedef struct {
   grpc_call_credentials **creds_array;
@@ -318,7 +318,7 @@ typedef struct grpc_credentials_metadata_request
 typedef void (*grpc_fetch_oauth2_func)(grpc_exec_ctx *exec_ctx,
                                        grpc_credentials_metadata_request *req,
                                        grpc_httpcli_context *http_context,
-                                       grpc_pollset_set *pollset_set,
+                                       grpc_pops *pops,
                                        grpc_httpcli_response_cb response_cb,
                                        gpr_timespec deadline);
 
diff --git a/src/core/lib/security/google_default_credentials.c b/src/core/lib/security/google_default_credentials.c
index d831a986b27ca58a2fba644f95d0fc25f9a8ec95..29045f36eb3b4aea94afa8e6eb21e584e78913a0 100644
--- a/src/core/lib/security/google_default_credentials.c
+++ b/src/core/lib/security/google_default_credentials.c
@@ -41,6 +41,7 @@
 
 #include "src/core/lib/http/httpcli.h"
 #include "src/core/lib/http/parser.h"
+#include "src/core/lib/iomgr/pops.h"
 #include "src/core/lib/support/env.h"
 #include "src/core/lib/support/load_file.h"
 #include "src/core/lib/surface/api_trace.h"
@@ -60,8 +61,7 @@ static gpr_once g_once = GPR_ONCE_INIT;
 static void init_default_credentials(void) { gpr_mu_init(&g_state_mu); }
 
 typedef struct {
-  grpc_pollset *pollset;
-  grpc_pollset_set *pollset_set;
+  grpc_pops *pops;
   int is_done;
   int success;
 } compute_engine_detector;
@@ -85,7 +85,7 @@ static void on_compute_engine_detection_http_response(
   }
   gpr_mu_lock(g_polling_mu);
   detector->is_done = 1;
-  grpc_pollset_kick(detector->pollset, NULL);
+  grpc_pollset_kick(grpc_pops_pollset(detector->pops), NULL);
   gpr_mu_unlock(g_polling_mu);
 }
 
@@ -104,11 +104,9 @@ static int is_stack_running_on_compute_engine(void) {
      on compute engine. */
   gpr_timespec max_detection_delay = gpr_time_from_seconds(1, GPR_TIMESPAN);
 
-  detector.pollset = gpr_malloc(grpc_pollset_size());
-  grpc_pollset_init(detector.pollset, &g_polling_mu);
-  detector.pollset_set = grpc_pollset_set_create();
-  grpc_pollset_set_add_pollset(&exec_ctx, detector.pollset_set,
-                               detector.pollset);
+  grpc_pollset *pollset = gpr_malloc(grpc_pollset_size());
+  grpc_pollset_init(pollset, &g_polling_mu);
+  detector.pops = grpc_pops_create_from_pollset(pollset);
   detector.is_done = 0;
   detector.success = 0;
 
@@ -119,7 +117,7 @@ static int is_stack_running_on_compute_engine(void) {
   grpc_httpcli_context_init(&context);
 
   grpc_httpcli_get(
-      &exec_ctx, &context, detector.pollset_set, &request,
+      &exec_ctx, &context, detector.pops, &request,
       gpr_time_add(gpr_now(GPR_CLOCK_REALTIME), max_detection_delay),
       on_compute_engine_detection_http_response, &detector);
 
@@ -130,20 +128,22 @@ static int is_stack_running_on_compute_engine(void) {
   gpr_mu_lock(g_polling_mu);
   while (!detector.is_done) {
     grpc_pollset_worker *worker = NULL;
-    grpc_pollset_work(&exec_ctx, detector.pollset, &worker,
+    grpc_pollset_work(&exec_ctx, grpc_pops_pollset(detector.pops), &worker,
                       gpr_now(GPR_CLOCK_MONOTONIC),
                       gpr_inf_future(GPR_CLOCK_MONOTONIC));
   }
   gpr_mu_unlock(g_polling_mu);
 
   grpc_httpcli_context_destroy(&context);
-  grpc_closure_init(&destroy_closure, destroy_pollset, detector.pollset);
-  grpc_pollset_shutdown(&exec_ctx, detector.pollset, &destroy_closure);
-  grpc_pollset_set_destroy(detector.pollset_set);
+  grpc_closure_init(&destroy_closure, destroy_pollset,
+                    grpc_pops_pollset(detector.pops));
+  grpc_pollset_shutdown(&exec_ctx, grpc_pops_pollset(detector.pops),
+                        &destroy_closure);
   grpc_exec_ctx_finish(&exec_ctx);
   g_polling_mu = NULL;
 
-  gpr_free(detector.pollset);
+  gpr_free(grpc_pops_pollset(detector.pops));
+  grpc_pops_destroy(detector.pops);
 
   return detector.success;
 }
diff --git a/src/core/lib/security/jwt_verifier.c b/src/core/lib/security/jwt_verifier.c
index f764596aa9e0e0df77a5eda15a4a4012f35d5aaf..a5d200d7a30bb090cd39c9717b6867db9c5ea2d1 100644
--- a/src/core/lib/security/jwt_verifier.c
+++ b/src/core/lib/security/jwt_verifier.c
@@ -37,6 +37,7 @@
 #include <string.h>
 
 #include "src/core/lib/http/httpcli.h"
+#include "src/core/lib/iomgr/pops.h"
 #include "src/core/lib/security/b64.h"
 #include "src/core/lib/tsi/ssl_types.h"
 
@@ -321,7 +322,7 @@ grpc_jwt_verifier_status grpc_jwt_claims_check(const grpc_jwt_claims *claims,
 
 typedef struct {
   grpc_jwt_verifier *verifier;
-  grpc_pollset_set *pollset_set;
+  grpc_pops *pops;
   jose_header *header;
   grpc_jwt_claims *claims;
   char *audience;
@@ -341,8 +342,7 @@ static verifier_cb_ctx *verifier_cb_ctx_create(
   verifier_cb_ctx *ctx = gpr_malloc(sizeof(verifier_cb_ctx));
   memset(ctx, 0, sizeof(verifier_cb_ctx));
   ctx->verifier = verifier;
-  ctx->pollset_set = grpc_pollset_set_create();
-  grpc_pollset_set_add_pollset(&exec_ctx, ctx->pollset_set, pollset);
+  ctx->pops = grpc_pops_create_from_pollset(pollset);
   ctx->header = header;
   ctx->audience = gpr_strdup(audience);
   ctx->claims = claims;
@@ -360,7 +360,7 @@ void verifier_cb_ctx_destroy(verifier_cb_ctx *ctx) {
   gpr_slice_unref(ctx->signature);
   gpr_slice_unref(ctx->signed_data);
   jose_header_destroy(ctx->header);
-  grpc_pollset_set_destroy(ctx->pollset_set);
+  grpc_pops_destroy(ctx->pops);
   /* TODO: see what to do with claims... */
   gpr_free(ctx);
 }
@@ -646,7 +646,7 @@ static void on_openid_config_retrieved(grpc_exec_ctx *exec_ctx, void *user_data,
     *(req.host + (req.http.path - jwks_uri)) = '\0';
   }
   grpc_httpcli_get(
-      exec_ctx, &ctx->verifier->http_ctx, ctx->pollset_set, &req,
+      exec_ctx, &ctx->verifier->http_ctx, ctx->pops, &req,
       gpr_time_add(gpr_now(GPR_CLOCK_REALTIME), grpc_jwt_verifier_max_delay),
       on_keys_retrieved, ctx);
   grpc_json_destroy(json);
@@ -749,7 +749,7 @@ static void retrieve_key_and_verify(grpc_exec_ctx *exec_ctx,
   }
 
   grpc_httpcli_get(
-      exec_ctx, &ctx->verifier->http_ctx, ctx->pollset_set, &req,
+      exec_ctx, &ctx->verifier->http_ctx, ctx->pops, &req,
       gpr_time_add(gpr_now(GPR_CLOCK_REALTIME), grpc_jwt_verifier_max_delay),
       http_cb, ctx);
   gpr_free(req.host);
diff --git a/src/core/lib/surface/call.c b/src/core/lib/surface/call.c
index 899784e2c2acd17d4ecc812cb6a822e48db773bc..4b5a7d746596b078d92ee51818755f3ae7749541 100644
--- a/src/core/lib/surface/call.c
+++ b/src/core/lib/surface/call.c
@@ -135,7 +135,7 @@ typedef struct batch_control {
 
 struct grpc_call {
   grpc_completion_queue *cq;
-  grpc_pollset_set *pollset_set;
+  grpc_pops *pops;
   grpc_channel *channel;
   grpc_call *parent;
   grpc_call *first_child;
@@ -259,13 +259,6 @@ grpc_call *grpc_call_create(
   gpr_mu_init(&call->mu);
   call->channel = channel;
   call->cq = cq;
-  if (cq != NULL && pollset_set_alternative != NULL) {
-    gpr_log(
-        GPR_ERROR,
-        "Only one of 'cq' and 'pollset_set_alternative' should be non-NULL.");
-    abort();
-  }
-  call->pollset_set = pollset_set_alternative;
   call->parent = parent_call;
   call->is_client = server_transport_data == NULL;
   if (call->is_client) {
@@ -290,14 +283,18 @@ grpc_call *grpc_call_create(
                        call->context, server_transport_data,
                        CALL_STACK_FROM_CALL(call));
   if (cq != NULL) {
+    GPR_ASSERT(
+        pollset_set_alternative == NULL &&
+        "Only one of 'cq' and 'pollset_set_alternative' should be non-NULL.");
     GRPC_CQ_INTERNAL_REF(cq, "bind");
-    grpc_call_stack_set_pollset_or_pollset_set(
-        &exec_ctx, CALL_STACK_FROM_CALL(call), grpc_cq_pollset(cq), NULL);
+    call->pops = grpc_pops_create_from_pollset(grpc_cq_pollset(cq));
   }
   if (pollset_set_alternative != NULL) {
-    GPR_ASSERT(cq == NULL);
+    call->pops = grpc_pops_create_from_pollset_set(pollset_set_alternative);
+  }
+  if (call->pops != NULL) {
     grpc_call_stack_set_pollset_or_pollset_set(
-        &exec_ctx, CALL_STACK_FROM_CALL(call), NULL, pollset_set_alternative);
+        &exec_ctx, CALL_STACK_FROM_CALL(call), call->pops);
   }
   if (parent_call != NULL) {
     GRPC_CALL_INTERNAL_REF(parent_call, "child");
@@ -352,11 +349,19 @@ grpc_call *grpc_call_create(
 void grpc_call_set_completion_queue(grpc_exec_ctx *exec_ctx, grpc_call *call,
                                     grpc_completion_queue *cq) {
   GPR_ASSERT(cq);
-  GPR_ASSERT(call->pollset_set == NULL);
+
+  if (call->pops != NULL && grpc_pops_pollset_set(call->pops) != NULL) {
+    gpr_log(GPR_ERROR, "A pollset_set is already registered for this call.");
+    abort();
+  }
   call->cq = cq;
   GRPC_CQ_INTERNAL_REF(cq, "bind");
+  if (call->pops != NULL) {
+    grpc_pops_destroy(call->pops);
+  }
+  call->pops = grpc_pops_create_from_pollset(grpc_cq_pollset(cq));
   grpc_call_stack_set_pollset_or_pollset_set(
-      exec_ctx, CALL_STACK_FROM_CALL(call), grpc_cq_pollset(cq), NULL);
+      exec_ctx, CALL_STACK_FROM_CALL(call), call->pops);
 }
 
 #ifdef GRPC_STREAM_REFCOUNT_DEBUG
@@ -404,6 +409,7 @@ static void destroy_call(grpc_exec_ctx *exec_ctx, void *call, bool success) {
   if (c->cq) {
     GRPC_CQ_INTERNAL_UNREF(c->cq, "bind");
   }
+  grpc_pops_destroy(c->pops);
   gpr_free(c);
   GPR_TIMER_END("destroy_call", 0);
 }
diff --git a/src/core/lib/transport/transport.c b/src/core/lib/transport/transport.c
index 7f2ae5f52e894e9ef556a0754f32d0f08fd06508..ba39693e471671bf03c65b24cdb457bfce67b494 100644
--- a/src/core/lib/transport/transport.c
+++ b/src/core/lib/transport/transport.c
@@ -125,17 +125,18 @@ void grpc_transport_perform_op(grpc_exec_ctx *exec_ctx,
   transport->vtable->perform_op(exec_ctx, transport, op);
 }
 
-void grpc_transport_set_pollset(grpc_exec_ctx *exec_ctx,
-                                grpc_transport *transport, grpc_stream *stream,
-                                grpc_pollset *pollset) {
-  transport->vtable->set_pollset(exec_ctx, transport, stream, pollset);
-}
-
-void grpc_transport_set_pollset_set(grpc_exec_ctx *exec_ctx,
-                                    grpc_transport *transport,
-                                    grpc_stream *stream,
-                                    grpc_pollset_set *pollset_set) {
-  transport->vtable->set_pollset_set(exec_ctx, transport, stream, pollset_set);
+void grpc_transport_set_pops(grpc_exec_ctx *exec_ctx, grpc_transport *transport,
+                             grpc_stream *stream, grpc_pops *pops) {
+  grpc_pollset *pollset;
+  grpc_pollset_set *pollset_set;
+  if ((pollset = grpc_pops_pollset(pops)) != NULL) {
+    transport->vtable->set_pollset(exec_ctx, transport, stream, pollset);
+  } else if ((pollset_set = grpc_pops_pollset_set(pops)) != NULL) {
+    transport->vtable->set_pollset_set(exec_ctx, transport, stream,
+                                       pollset_set);
+  } else {
+    abort();
+  }
 }
 
 void grpc_transport_destroy_stream(grpc_exec_ctx *exec_ctx,
diff --git a/src/core/lib/transport/transport.h b/src/core/lib/transport/transport.h
index 8f0c2d9023ad8b9ef2b671d3a37c7d5e8d9b5d00..0be080c9ca54988e39d65c792ea7c19115dfb619 100644
--- a/src/core/lib/transport/transport.h
+++ b/src/core/lib/transport/transport.h
@@ -39,6 +39,7 @@
 #include "src/core/lib/channel/context.h"
 #include "src/core/lib/iomgr/pollset.h"
 #include "src/core/lib/iomgr/pollset_set.h"
+#include "src/core/lib/iomgr/pops.h"
 #include "src/core/lib/transport/byte_stream.h"
 #include "src/core/lib/transport/metadata_batch.h"
 
@@ -197,14 +198,8 @@ int grpc_transport_init_stream(grpc_exec_ctx *exec_ctx,
                                grpc_stream_refcount *refcount,
                                const void *server_data);
 
-void grpc_transport_set_pollset(grpc_exec_ctx *exec_ctx,
-                                grpc_transport *transport, grpc_stream *stream,
-                                grpc_pollset *pollset);
-
-void grpc_transport_set_pollset_set(grpc_exec_ctx *exec_ctx,
-                                    grpc_transport *transport,
-                                    grpc_stream *stream,
-                                    grpc_pollset_set *pollset_set);
+void grpc_transport_set_pops(grpc_exec_ctx *exec_ctx, grpc_transport *transport,
+                             grpc_stream *stream, grpc_pops *pops);
 
 /* Destroy transport data for a stream.
 
diff --git a/src/python/grpcio/grpc_core_dependencies.py b/src/python/grpcio/grpc_core_dependencies.py
index 937998208664711eb036e60ba6da168f5f12bb6d..c88cfff99c25fe488122caa14c191bb34b32a228 100644
--- a/src/python/grpcio/grpc_core_dependencies.py
+++ b/src/python/grpcio/grpc_core_dependencies.py
@@ -104,6 +104,7 @@ CORE_SOURCE_FILES = [
   'src/core/lib/iomgr/iomgr_windows.c',
   'src/core/lib/iomgr/pollset_set_windows.c',
   'src/core/lib/iomgr/pollset_windows.c',
+  'src/core/lib/iomgr/pops.c',
   'src/core/lib/iomgr/resolve_address_posix.c',
   'src/core/lib/iomgr/resolve_address_windows.c',
   'src/core/lib/iomgr/sockaddr_utils.c',
@@ -227,7 +228,6 @@ CORE_SOURCE_FILES = [
   'third_party/nanopb/pb_common.c',
   'third_party/nanopb/pb_decode.c',
   'third_party/nanopb/pb_encode.c',
-  'src/core/ext/lb_policy/common.c',
   'src/core/ext/lb_policy/pick_first/pick_first.c',
   'src/core/ext/lb_policy/round_robin/round_robin.c',
   'src/core/ext/resolver/dns/native/dns_resolver.c',
diff --git a/test/core/http/httpcli_test.c b/test/core/http/httpcli_test.c
index 36e43b8de87f32e1a6e2d612f4f41e0ddc439961..6b9ec7fd94a1c85feafe922fe8e7ba5bb4935d94 100644
--- a/test/core/http/httpcli_test.c
+++ b/test/core/http/httpcli_test.c
@@ -48,8 +48,7 @@
 static int g_done = 0;
 static grpc_httpcli_context g_context;
 static gpr_mu *g_mu;
-static grpc_pollset *g_pollset;
-static grpc_pollset_set *g_pollset_set;
+static grpc_pops *g_pops;
 
 static gpr_timespec n_seconds_time(int seconds) {
   return GRPC_TIMEOUT_SECONDS_TO_DEADLINE(seconds);
@@ -67,7 +66,7 @@ static void on_finish(grpc_exec_ctx *exec_ctx, void *arg,
   GPR_ASSERT(0 == memcmp(expect, response->body, response->body_length));
   gpr_mu_lock(g_mu);
   g_done = 1;
-  grpc_pollset_kick(g_pollset, NULL);
+  grpc_pollset_kick(grpc_pops_pollset(g_pops), NULL);
   gpr_mu_unlock(g_mu);
 }
 
@@ -87,12 +86,12 @@ static void test_get(int port) {
   req.http.path = "/get";
   req.handshaker = &grpc_httpcli_plaintext;
 
-  grpc_httpcli_get(&exec_ctx, &g_context, g_pollset_set, &req,
-                   n_seconds_time(15), on_finish, (void *)42);
+  grpc_httpcli_get(&exec_ctx, &g_context, g_pops, &req, n_seconds_time(15),
+                   on_finish, (void *)42);
   gpr_mu_lock(g_mu);
   while (!g_done) {
     grpc_pollset_worker *worker = NULL;
-    grpc_pollset_work(&exec_ctx, g_pollset, &worker,
+    grpc_pollset_work(&exec_ctx, grpc_pops_pollset(g_pops), &worker,
                       gpr_now(GPR_CLOCK_MONOTONIC), n_seconds_time(20));
     gpr_mu_unlock(g_mu);
     grpc_exec_ctx_finish(&exec_ctx);
@@ -118,12 +117,12 @@ static void test_post(int port) {
   req.http.path = "/post";
   req.handshaker = &grpc_httpcli_plaintext;
 
-  grpc_httpcli_post(&exec_ctx, &g_context, g_pollset_set, &req, "hello", 5,
+  grpc_httpcli_post(&exec_ctx, &g_context, g_pops, &req, "hello", 5,
                     n_seconds_time(15), on_finish, (void *)42);
   gpr_mu_lock(g_mu);
   while (!g_done) {
     grpc_pollset_worker *worker = NULL;
-    grpc_pollset_work(&exec_ctx, g_pollset, &worker,
+    grpc_pollset_work(&exec_ctx, grpc_pops_pollset(g_pops), &worker,
                       gpr_now(GPR_CLOCK_MONOTONIC), n_seconds_time(20));
     gpr_mu_unlock(g_mu);
     grpc_exec_ctx_finish(&exec_ctx);
@@ -133,8 +132,8 @@ static void test_post(int port) {
   gpr_free(host);
 }
 
-static void destroy_pollset(grpc_exec_ctx *exec_ctx, void *p, bool success) {
-  grpc_pollset_destroy(p);
+static void destroy_pops(grpc_exec_ctx *exec_ctx, void *p, bool success) {
+  grpc_pollset_destroy(grpc_pops_pollset(p));
 }
 
 int main(int argc, char **argv) {
@@ -181,21 +180,21 @@ int main(int argc, char **argv) {
   grpc_test_init(argc, argv);
   grpc_init();
   grpc_httpcli_context_init(&g_context);
-  g_pollset = gpr_malloc(grpc_pollset_size());
-  grpc_pollset_init(g_pollset, &g_mu);
-  g_pollset_set = grpc_pollset_set_create();
-  grpc_pollset_set_add_pollset(&exec_ctx, g_pollset_set, g_pollset);
+  grpc_pollset *pollset = gpr_malloc(grpc_pollset_size());
+  grpc_pollset_init(pollset, &g_mu);
+  g_pops = grpc_pops_create_from_pollset(pollset);
 
   test_get(port);
   test_post(port);
 
   grpc_httpcli_context_destroy(&g_context);
-  grpc_closure_init(&destroyed, destroy_pollset, g_pollset);
-  grpc_pollset_shutdown(&exec_ctx, g_pollset, &destroyed);
+  grpc_closure_init(&destroyed, destroy_pops, g_pops);
+  grpc_pollset_shutdown(&exec_ctx, grpc_pops_pollset(g_pops), &destroyed);
   grpc_exec_ctx_finish(&exec_ctx);
   grpc_shutdown();
 
-  gpr_free(g_pollset);
+  gpr_free(grpc_pops_pollset(g_pops));
+  grpc_pops_destroy(g_pops);
 
   gpr_subprocess_destroy(server);
 
diff --git a/test/core/http/httpscli_test.c b/test/core/http/httpscli_test.c
index 1aeaf2cf10f62ae6098779b6fbdb7d9b5da8dec9..ee2122e1c101483522cc0d4a93f0b6916dc5c25c 100644
--- a/test/core/http/httpscli_test.c
+++ b/test/core/http/httpscli_test.c
@@ -48,8 +48,7 @@
 static int g_done = 0;
 static grpc_httpcli_context g_context;
 static gpr_mu *g_mu;
-static grpc_pollset *g_pollset;
-static grpc_pollset_set *g_pollset_set;
+static grpc_pops *g_pops;
 
 static gpr_timespec n_seconds_time(int seconds) {
   return GRPC_TIMEOUT_SECONDS_TO_DEADLINE(seconds);
@@ -67,7 +66,7 @@ static void on_finish(grpc_exec_ctx *exec_ctx, void *arg,
   GPR_ASSERT(0 == memcmp(expect, response->body, response->body_length));
   gpr_mu_lock(g_mu);
   g_done = 1;
-  grpc_pollset_kick(g_pollset, NULL);
+  grpc_pollset_kick(grpc_pops_pollset(g_pops), NULL);
   gpr_mu_unlock(g_mu);
 }
 
@@ -88,12 +87,12 @@ static void test_get(int port) {
   req.http.path = "/get";
   req.handshaker = &grpc_httpcli_ssl;
 
-  grpc_httpcli_get(&exec_ctx, &g_context, g_pollset_set, &req,
-                   n_seconds_time(15), on_finish, (void *)42);
+  grpc_httpcli_get(&exec_ctx, &g_context, g_pops, &req, n_seconds_time(15),
+                   on_finish, (void *)42);
   gpr_mu_lock(g_mu);
   while (!g_done) {
     grpc_pollset_worker *worker = NULL;
-    grpc_pollset_work(&exec_ctx, g_pollset, &worker,
+    grpc_pollset_work(&exec_ctx, grpc_pops_pollset(g_pops), &worker,
                       gpr_now(GPR_CLOCK_MONOTONIC), n_seconds_time(20));
     gpr_mu_unlock(g_mu);
     grpc_exec_ctx_finish(&exec_ctx);
@@ -120,12 +119,12 @@ static void test_post(int port) {
   req.http.path = "/post";
   req.handshaker = &grpc_httpcli_ssl;
 
-  grpc_httpcli_post(&exec_ctx, &g_context, g_pollset_set, &req, "hello", 5,
+  grpc_httpcli_post(&exec_ctx, &g_context, g_pops, &req, "hello", 5,
                     n_seconds_time(15), on_finish, (void *)42);
   gpr_mu_lock(g_mu);
   while (!g_done) {
     grpc_pollset_worker *worker = NULL;
-    grpc_pollset_work(&exec_ctx, g_pollset, &worker,
+    grpc_pollset_work(&exec_ctx, grpc_pops_pollset(g_pops), &worker,
                       gpr_now(GPR_CLOCK_MONOTONIC), n_seconds_time(20));
     gpr_mu_unlock(g_mu);
     grpc_exec_ctx_finish(&exec_ctx);
@@ -135,8 +134,8 @@ static void test_post(int port) {
   gpr_free(host);
 }
 
-static void destroy_pollset(grpc_exec_ctx *exec_ctx, void *p, bool success) {
-  grpc_pollset_destroy(p);
+static void destroy_pops(grpc_exec_ctx *exec_ctx, void *p, bool success) {
+  grpc_pollset_destroy(grpc_pops_pollset(p));
 }
 
 int main(int argc, char **argv) {
@@ -184,22 +183,21 @@ int main(int argc, char **argv) {
   grpc_test_init(argc, argv);
   grpc_init();
   grpc_httpcli_context_init(&g_context);
-  g_pollset = gpr_malloc(grpc_pollset_size());
-  grpc_pollset_init(g_pollset, &g_mu);
-  g_pollset_set = grpc_pollset_set_create();
-  grpc_pollset_set_add_pollset(&exec_ctx, g_pollset_set, g_pollset);
+  grpc_pollset *pollset = gpr_malloc(grpc_pollset_size());
+  grpc_pollset_init(pollset, &g_mu);
+  g_pops = grpc_pops_create_from_pollset(pollset);
 
   test_get(port);
   test_post(port);
 
   grpc_httpcli_context_destroy(&g_context);
-  grpc_closure_init(&destroyed, destroy_pollset, g_pollset);
-  grpc_pollset_shutdown(&exec_ctx, g_pollset, &destroyed);
+  grpc_closure_init(&destroyed, destroy_pops, g_pops);
+  grpc_pollset_shutdown(&exec_ctx, grpc_pops_pollset(g_pops), &destroyed);
   grpc_exec_ctx_finish(&exec_ctx);
-  grpc_pollset_set_destroy(g_pollset_set);
   grpc_shutdown();
 
-  gpr_free(g_pollset);
+  gpr_free(grpc_pops_pollset(g_pops));
+  grpc_pops_destroy(g_pops);
 
   gpr_subprocess_destroy(server);
 
diff --git a/test/core/security/oauth2_utils.c b/test/core/security/oauth2_utils.c
index 94d35026f1c78764c1db98ea66b0b78a07e37720..2580757837d59af870c9e768b226b1afce4756e1 100644
--- a/test/core/security/oauth2_utils.c
+++ b/test/core/security/oauth2_utils.c
@@ -46,8 +46,7 @@
 
 typedef struct {
   gpr_mu *mu;
-  grpc_pollset *pollset;
-  grpc_pollset_set *pollset_set;
+  grpc_pops *pops;
   int is_done;
   char *token;
 } oauth2_request;
@@ -71,7 +70,7 @@ static void on_oauth2_response(grpc_exec_ctx *exec_ctx, void *user_data,
   gpr_mu_lock(request->mu);
   request->is_done = 1;
   request->token = token;
-  grpc_pollset_kick(request->pollset, NULL);
+  grpc_pollset_kick(grpc_pops_pollset(request->pops), NULL);
   gpr_mu_unlock(request->mu);
 }
 
@@ -84,33 +83,30 @@ char *grpc_test_fetch_oauth2_token_with_credentials(
   grpc_closure do_nothing_closure;
   grpc_auth_metadata_context null_ctx = {"", "", NULL, NULL};
 
-  request.pollset = gpr_malloc(grpc_pollset_size());
-  grpc_pollset_init(request.pollset, &request.mu);
-  request.pollset_set = grpc_pollset_set_create();
-  grpc_pollset_set_add_pollset(&exec_ctx, request.pollset_set, request.pollset);
+  grpc_pollset *pollset = gpr_malloc(grpc_pollset_size());
+  grpc_pollset_init(pollset, &request.mu);
   request.is_done = 0;
 
   grpc_closure_init(&do_nothing_closure, do_nothing, NULL);
 
-  grpc_call_credentials_get_request_metadata(&exec_ctx, creds,
-                                             request.pollset_set, null_ctx,
-                                             on_oauth2_response, &request);
+  grpc_call_credentials_get_request_metadata(
+      &exec_ctx, creds, request.pops, null_ctx, on_oauth2_response, &request);
 
   grpc_exec_ctx_finish(&exec_ctx);
 
   gpr_mu_lock(request.mu);
   while (!request.is_done) {
     grpc_pollset_worker *worker = NULL;
-    grpc_pollset_work(&exec_ctx, request.pollset, &worker,
+    grpc_pollset_work(&exec_ctx, grpc_pops_pollset(request.pops), &worker,
                       gpr_now(GPR_CLOCK_MONOTONIC),
                       gpr_inf_future(GPR_CLOCK_MONOTONIC));
   }
   gpr_mu_unlock(request.mu);
 
-  grpc_pollset_shutdown(&exec_ctx, request.pollset, &do_nothing_closure);
+  grpc_pollset_shutdown(&exec_ctx, grpc_pops_pollset(request.pops),
+                        &do_nothing_closure);
   grpc_exec_ctx_finish(&exec_ctx);
-  grpc_pollset_destroy(request.pollset);
-  grpc_pollset_set_destroy(request.pollset_set);
-  gpr_free(request.pollset);
+  gpr_free(grpc_pops_pollset(request.pops));
+  grpc_pops_destroy(request.pops);
   return request.token;
 }
diff --git a/test/core/security/print_google_default_creds_token.c b/test/core/security/print_google_default_creds_token.c
index 2c292bc80b5d16f11ee2350d3f90761c128f6584..4f5038c045fb911f8f09e04a1eaeca709506b228 100644
--- a/test/core/security/print_google_default_creds_token.c
+++ b/test/core/security/print_google_default_creds_token.c
@@ -47,8 +47,7 @@
 
 typedef struct {
   gpr_mu *mu;
-  grpc_pollset *pollset;
-  grpc_pollset_set *pollset_set;
+  grpc_pops *pops;
   int is_done;
 } synchronizer;
 
@@ -67,7 +66,7 @@ static void on_metadata_response(grpc_exec_ctx *exec_ctx, void *user_data,
   }
   gpr_mu_lock(sync->mu);
   sync->is_done = 1;
-  grpc_pollset_kick(sync->pollset, NULL);
+  grpc_pollset_kick(grpc_pops_pollset(sync->pops), NULL);
   gpr_mu_unlock(sync->mu);
 }
 
@@ -94,20 +93,19 @@ int main(int argc, char **argv) {
     goto end;
   }
 
-  sync.pollset = gpr_malloc(grpc_pollset_size());
-  grpc_pollset_init(sync.pollset, &sync.mu);
-  sync.pollset_set = grpc_pollset_set_create();
-  grpc_pollset_set_add_pollset(&exec_ctx, sync.pollset_set, sync.pollset);
+  grpc_pollset *pollset = gpr_malloc(grpc_pollset_size());
+  grpc_pollset_init(pollset, &sync.mu);
+  sync.pops = grpc_pops_create_from_pollset(pollset);
   sync.is_done = 0;
 
   grpc_call_credentials_get_request_metadata(
       &exec_ctx, ((grpc_composite_channel_credentials *)creds)->call_creds,
-      sync.pollset_set, context, on_metadata_response, &sync);
+      sync.pops, context, on_metadata_response, &sync);
 
   gpr_mu_lock(sync.mu);
   while (!sync.is_done) {
     grpc_pollset_worker *worker = NULL;
-    grpc_pollset_work(&exec_ctx, sync.pollset, &worker,
+    grpc_pollset_work(&exec_ctx, grpc_pops_pollset(sync.pops), &worker,
                       gpr_now(GPR_CLOCK_MONOTONIC),
                       gpr_inf_future(GPR_CLOCK_MONOTONIC));
     gpr_mu_unlock(sync.mu);
@@ -119,8 +117,8 @@ int main(int argc, char **argv) {
   grpc_exec_ctx_finish(&exec_ctx);
 
   grpc_channel_credentials_release(creds);
-  grpc_pollset_set_destroy(sync.pollset_set);
-  gpr_free(sync.pollset);
+  gpr_free(grpc_pops_pollset(sync.pops));
+  grpc_pops_destroy(sync.pops);
 
 end:
   gpr_cmdline_destroy(cl);
diff --git a/test/core/util/port_server_client.c b/test/core/util/port_server_client.c
index 25a14f513e42943f14bab94d2c3b0c0ff4e9c4fb..e6cf35be94e40e914161ac153530e4b1fcfcb121 100644
--- a/test/core/util/port_server_client.c
+++ b/test/core/util/port_server_client.c
@@ -51,15 +51,16 @@
 
 typedef struct freereq {
   gpr_mu *mu;
-  grpc_pollset *pollset;
-  grpc_pollset_set *pollset_set;
+  grpc_pops *pops;
   int done;
 } freereq;
 
-static void destroy_pollset_and_shutdown(grpc_exec_ctx *exec_ctx, void *p,
-                                         bool success) {
-  grpc_pollset_destroy(p);
-  gpr_free(p);
+static void destroy_pops_and_shutdown(grpc_exec_ctx *exec_ctx, void *p,
+                                      bool success) {
+  grpc_pollset *pollset = grpc_pops_pollset(p);
+  grpc_pollset_destroy(pollset);
+  gpr_free(pollset);
+  grpc_pops_destroy(p);
   grpc_shutdown();
 }
 
@@ -68,7 +69,7 @@ static void freed_port_from_server(grpc_exec_ctx *exec_ctx, void *arg,
   freereq *pr = arg;
   gpr_mu_lock(pr->mu);
   pr->done = 1;
-  grpc_pollset_kick(pr->pollset, NULL);
+  grpc_pollset_kick(grpc_pops_pollset(pr->pops), NULL);
   gpr_mu_unlock(pr->mu);
 }
 
@@ -85,25 +86,23 @@ void grpc_free_port_using_server(char *server, int port) {
   memset(&pr, 0, sizeof(pr));
   memset(&req, 0, sizeof(req));
 
-  pr.pollset = gpr_malloc(grpc_pollset_size());
-  grpc_pollset_init(pr.pollset, &pr.mu);
-  pr.pollset_set = grpc_pollset_set_create();
-  grpc_pollset_set_add_pollset(&exec_ctx, pr.pollset_set, pr.pollset);
-  shutdown_closure =
-      grpc_closure_create(destroy_pollset_and_shutdown, pr.pollset);
+  grpc_pollset *pollset = gpr_malloc(grpc_pollset_size());
+  grpc_pollset_init(pollset, &pr.mu);
+  pr.pops = grpc_pops_create_from_pollset(pollset);
+  shutdown_closure = grpc_closure_create(destroy_pops_and_shutdown, pr.pops);
 
   req.host = server;
   gpr_asprintf(&path, "/drop/%d", port);
   req.http.path = path;
 
   grpc_httpcli_context_init(&context);
-  grpc_httpcli_get(&exec_ctx, &context, pr.pollset_set, &req,
+  grpc_httpcli_get(&exec_ctx, &context, pr.pops, &req,
                    GRPC_TIMEOUT_SECONDS_TO_DEADLINE(10), freed_port_from_server,
                    &pr);
   gpr_mu_lock(pr.mu);
   while (!pr.done) {
     grpc_pollset_worker *worker = NULL;
-    grpc_pollset_work(&exec_ctx, pr.pollset, &worker,
+    grpc_pollset_work(&exec_ctx, grpc_pops_pollset(pr.pops), &worker,
                       gpr_now(GPR_CLOCK_MONOTONIC),
                       GRPC_TIMEOUT_SECONDS_TO_DEADLINE(1));
   }
@@ -111,16 +110,15 @@ void grpc_free_port_using_server(char *server, int port) {
 
   grpc_httpcli_context_destroy(&context);
   grpc_exec_ctx_finish(&exec_ctx);
-  grpc_pollset_shutdown(&exec_ctx, pr.pollset, shutdown_closure);
+  grpc_pollset_shutdown(&exec_ctx, grpc_pops_pollset(pr.pops),
+                        shutdown_closure);
   grpc_exec_ctx_finish(&exec_ctx);
-  grpc_pollset_set_destroy(pr.pollset_set);
   gpr_free(path);
 }
 
 typedef struct portreq {
   gpr_mu *mu;
-  grpc_pollset *pollset;
-  grpc_pollset_set *pollset_set;
+  grpc_pops *pops;
   int port;
   int retries;
   char *server;
@@ -156,7 +154,7 @@ static void got_port_from_server(grpc_exec_ctx *exec_ctx, void *arg,
     pr->retries++;
     req.host = pr->server;
     req.http.path = "/get";
-    grpc_httpcli_get(exec_ctx, pr->ctx, pr->pollset_set, &req,
+    grpc_httpcli_get(exec_ctx, pr->ctx, pr->pops, &req,
                      GRPC_TIMEOUT_SECONDS_TO_DEADLINE(10), got_port_from_server,
                      pr);
     return;
@@ -170,7 +168,7 @@ static void got_port_from_server(grpc_exec_ctx *exec_ctx, void *arg,
   GPR_ASSERT(port > 1024);
   gpr_mu_lock(pr->mu);
   pr->port = port;
-  grpc_pollset_kick(pr->pollset, NULL);
+  grpc_pollset_kick(grpc_pops_pollset(pr->pops), NULL);
   gpr_mu_unlock(pr->mu);
 }
 
@@ -185,12 +183,10 @@ int grpc_pick_port_using_server(char *server) {
 
   memset(&pr, 0, sizeof(pr));
   memset(&req, 0, sizeof(req));
-  pr.pollset = gpr_malloc(grpc_pollset_size());
-  grpc_pollset_init(pr.pollset, &pr.mu);
-  pr.pollset_set = grpc_pollset_set_create();
-  grpc_pollset_set_add_pollset(&exec_ctx, pr.pollset_set, pr.pollset);
-  shutdown_closure =
-      grpc_closure_create(destroy_pollset_and_shutdown, pr.pollset);
+  grpc_pollset *pollset = gpr_malloc(grpc_pollset_size());
+  grpc_pollset_init(pollset, &pr.mu);
+  pr.pops = grpc_pops_create_from_pollset(pollset);
+  shutdown_closure = grpc_closure_create(destroy_pops_and_shutdown, pr.pops);
   pr.port = -1;
   pr.server = server;
   pr.ctx = &context;
@@ -199,23 +195,23 @@ int grpc_pick_port_using_server(char *server) {
   req.http.path = "/get";
 
   grpc_httpcli_context_init(&context);
-  grpc_httpcli_get(&exec_ctx, &context, pr.pollset_set, &req,
+  grpc_httpcli_get(&exec_ctx, &context, pr.pops, &req,
                    GRPC_TIMEOUT_SECONDS_TO_DEADLINE(10), got_port_from_server,
                    &pr);
   grpc_exec_ctx_finish(&exec_ctx);
   gpr_mu_lock(pr.mu);
   while (pr.port == -1) {
     grpc_pollset_worker *worker = NULL;
-    grpc_pollset_work(&exec_ctx, pr.pollset, &worker,
+    grpc_pollset_work(&exec_ctx, grpc_pops_pollset(pr.pops), &worker,
                       gpr_now(GPR_CLOCK_MONOTONIC),
                       GRPC_TIMEOUT_SECONDS_TO_DEADLINE(1));
   }
   gpr_mu_unlock(pr.mu);
 
   grpc_httpcli_context_destroy(&context);
-  grpc_pollset_shutdown(&exec_ctx, pr.pollset, shutdown_closure);
+  grpc_pollset_shutdown(&exec_ctx, grpc_pops_pollset(pr.pops),
+                        shutdown_closure);
   grpc_exec_ctx_finish(&exec_ctx);
-  grpc_pollset_set_destroy(pr.pollset_set);
 
   return pr.port;
 }
diff --git a/tools/doxygen/Doxyfile.core.internal b/tools/doxygen/Doxyfile.core.internal
index fadc5c99ca3e6721574c7590b6d422fca8b31a14..6a281f9d50d71eecfbf7c31e060e33086191769c 100644
--- a/tools/doxygen/Doxyfile.core.internal
+++ b/tools/doxygen/Doxyfile.core.internal
@@ -817,6 +817,7 @@ src/core/lib/iomgr/pollset.h \
 src/core/lib/iomgr/pollset_set.h \
 src/core/lib/iomgr/pollset_set_windows.h \
 src/core/lib/iomgr/pollset_windows.h \
+src/core/lib/iomgr/pops.h \
 src/core/lib/iomgr/resolve_address.h \
 src/core/lib/iomgr/sockaddr.h \
 src/core/lib/iomgr/sockaddr_posix.h \
@@ -918,7 +919,6 @@ third_party/nanopb/pb.h \
 third_party/nanopb/pb_common.h \
 third_party/nanopb/pb_decode.h \
 third_party/nanopb/pb_encode.h \
-src/core/ext/lb_policy/common.h \
 src/core/ext/census/aggregation.h \
 src/core/ext/census/census_interface.h \
 src/core/ext/census/census_rpc_stats.h \
@@ -953,6 +953,7 @@ src/core/lib/iomgr/iomgr_posix.c \
 src/core/lib/iomgr/iomgr_windows.c \
 src/core/lib/iomgr/pollset_set_windows.c \
 src/core/lib/iomgr/pollset_windows.c \
+src/core/lib/iomgr/pops.c \
 src/core/lib/iomgr/resolve_address_posix.c \
 src/core/lib/iomgr/resolve_address_windows.c \
 src/core/lib/iomgr/sockaddr_utils.c \
@@ -1076,7 +1077,6 @@ src/core/ext/lb_policy/grpclb/proto/grpc/lb/v0/load_balancer.pb.c \
 third_party/nanopb/pb_common.c \
 third_party/nanopb/pb_decode.c \
 third_party/nanopb/pb_encode.c \
-src/core/ext/lb_policy/common.c \
 src/core/ext/lb_policy/pick_first/pick_first.c \
 src/core/ext/lb_policy/round_robin/round_robin.c \
 src/core/ext/resolver/dns/native/dns_resolver.c \
diff --git a/tools/run_tests/sources_and_headers.json b/tools/run_tests/sources_and_headers.json
index cf5fbbc79be03706d646356c9f7f41745317d332..6978e14a9ebe40ec2e42423f21c3436aa578fd7e 100644
--- a/tools/run_tests/sources_and_headers.json
+++ b/tools/run_tests/sources_and_headers.json
@@ -5621,6 +5621,7 @@
       "src/core/lib/iomgr/pollset_set.h", 
       "src/core/lib/iomgr/pollset_set_windows.h", 
       "src/core/lib/iomgr/pollset_windows.h", 
+      "src/core/lib/iomgr/pops.h", 
       "src/core/lib/iomgr/resolve_address.h", 
       "src/core/lib/iomgr/sockaddr.h", 
       "src/core/lib/iomgr/sockaddr_posix.h", 
@@ -5730,6 +5731,8 @@
       "src/core/lib/iomgr/pollset_set_windows.h", 
       "src/core/lib/iomgr/pollset_windows.c", 
       "src/core/lib/iomgr/pollset_windows.h", 
+      "src/core/lib/iomgr/pops.c", 
+      "src/core/lib/iomgr/pops.h", 
       "src/core/lib/iomgr/resolve_address.h", 
       "src/core/lib/iomgr/resolve_address_posix.c", 
       "src/core/lib/iomgr/resolve_address_windows.c", 
@@ -5922,29 +5925,11 @@
     "third_party": false, 
     "type": "filegroup"
   }, 
-  {
-    "deps": [
-      "gpr", 
-      "grpc_base"
-    ], 
-    "headers": [
-      "src/core/ext/lb_policy/common.h"
-    ], 
-    "language": "c", 
-    "name": "grpc_lb_policy_common", 
-    "src": [
-      "src/core/ext/lb_policy/common.c", 
-      "src/core/ext/lb_policy/common.h"
-    ], 
-    "third_party": false, 
-    "type": "filegroup"
-  }, 
   {
     "deps": [
       "gpr", 
       "grpc_base", 
       "grpc_client_config", 
-      "grpc_lb_policy_common", 
       "nanopb"
     ], 
     "headers": [
@@ -5966,8 +5951,7 @@
     "deps": [
       "gpr", 
       "grpc_base", 
-      "grpc_client_config", 
-      "grpc_lb_policy_common"
+      "grpc_client_config"
     ], 
     "headers": [], 
     "language": "c", 
@@ -5982,8 +5966,7 @@
     "deps": [
       "gpr", 
       "grpc_base", 
-      "grpc_client_config", 
-      "grpc_lb_policy_common"
+      "grpc_client_config"
     ], 
     "headers": [], 
     "language": "c", 
diff --git a/vsprojects/vcxproj/grpc/grpc.vcxproj b/vsprojects/vcxproj/grpc/grpc.vcxproj
index 9eaad77ac7d43144ef4d80a602c6016ada074cc6..6f8f981b009b55663994934ab341bc25bb564fd0 100644
--- a/vsprojects/vcxproj/grpc/grpc.vcxproj
+++ b/vsprojects/vcxproj/grpc/grpc.vcxproj
@@ -326,6 +326,7 @@
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\iomgr\pollset_set.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\iomgr\pollset_set_windows.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\iomgr\pollset_windows.h" />
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\iomgr\pops.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\iomgr\resolve_address.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\iomgr\sockaddr.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\iomgr\sockaddr_posix.h" />
@@ -427,7 +428,6 @@
     <ClInclude Include="$(SolutionDir)\..\third_party\nanopb\pb_common.h" />
     <ClInclude Include="$(SolutionDir)\..\third_party\nanopb\pb_decode.h" />
     <ClInclude Include="$(SolutionDir)\..\third_party\nanopb\pb_encode.h" />
-    <ClInclude Include="$(SolutionDir)\..\src\core\ext\lb_policy\common.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\ext\census\aggregation.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\ext\census\census_interface.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\ext\census\census_rpc_stats.h" />
@@ -492,6 +492,8 @@
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\lib\iomgr\pollset_windows.c">
     </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\iomgr\pops.c">
+    </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\lib\iomgr\resolve_address_posix.c">
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\lib\iomgr\resolve_address_windows.c">
@@ -738,8 +740,6 @@
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\third_party\nanopb\pb_encode.c">
     </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\src\core\ext\lb_policy\common.c">
-    </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\ext\lb_policy\pick_first\pick_first.c">
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\ext\lb_policy\round_robin\round_robin.c">
diff --git a/vsprojects/vcxproj/grpc/grpc.vcxproj.filters b/vsprojects/vcxproj/grpc/grpc.vcxproj.filters
index 0a61853f2971c483f5ab96dd17fea87ed087b458..3d67baa2b4e88e641eebe75491445a85f89b3537 100644
--- a/vsprojects/vcxproj/grpc/grpc.vcxproj.filters
+++ b/vsprojects/vcxproj/grpc/grpc.vcxproj.filters
@@ -85,6 +85,9 @@
     <ClCompile Include="$(SolutionDir)\..\src\core\lib\iomgr\pollset_windows.c">
       <Filter>src\core\lib\iomgr</Filter>
     </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\iomgr\pops.c">
+      <Filter>src\core\lib\iomgr</Filter>
+    </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\lib\iomgr\resolve_address_posix.c">
       <Filter>src\core\lib\iomgr</Filter>
     </ClCompile>
@@ -454,9 +457,6 @@
     <ClCompile Include="$(SolutionDir)\..\third_party\nanopb\pb_encode.c">
       <Filter>third_party\nanopb</Filter>
     </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\src\core\ext\lb_policy\common.c">
-      <Filter>src\core\ext\lb_policy</Filter>
-    </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\ext\lb_policy\pick_first\pick_first.c">
       <Filter>src\core\ext\lb_policy\pick_first</Filter>
     </ClCompile>
@@ -674,6 +674,9 @@
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\iomgr\pollset_windows.h">
       <Filter>src\core\lib\iomgr</Filter>
     </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\iomgr\pops.h">
+      <Filter>src\core\lib\iomgr</Filter>
+    </ClInclude>
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\iomgr\resolve_address.h">
       <Filter>src\core\lib\iomgr</Filter>
     </ClInclude>
@@ -977,9 +980,6 @@
     <ClInclude Include="$(SolutionDir)\..\third_party\nanopb\pb_encode.h">
       <Filter>third_party\nanopb</Filter>
     </ClInclude>
-    <ClInclude Include="$(SolutionDir)\..\src\core\ext\lb_policy\common.h">
-      <Filter>src\core\ext\lb_policy</Filter>
-    </ClInclude>
     <ClInclude Include="$(SolutionDir)\..\src\core\ext\census\aggregation.h">
       <Filter>src\core\ext\census</Filter>
     </ClInclude>
diff --git a/vsprojects/vcxproj/grpc_unsecure/grpc_unsecure.vcxproj b/vsprojects/vcxproj/grpc_unsecure/grpc_unsecure.vcxproj
index 01214556ce984e9c08153a7d14d499a46813ef03..4402205e7b5decfe05efe0657e4536c65eb40367 100644
--- a/vsprojects/vcxproj/grpc_unsecure/grpc_unsecure.vcxproj
+++ b/vsprojects/vcxproj/grpc_unsecure/grpc_unsecure.vcxproj
@@ -315,6 +315,7 @@
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\iomgr\pollset_set.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\iomgr\pollset_set_windows.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\iomgr\pollset_windows.h" />
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\iomgr\pops.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\iomgr\resolve_address.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\iomgr\sockaddr.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\iomgr\sockaddr_posix.h" />
@@ -402,7 +403,6 @@
     <ClInclude Include="$(SolutionDir)\..\third_party\nanopb\pb_common.h" />
     <ClInclude Include="$(SolutionDir)\..\third_party\nanopb\pb_decode.h" />
     <ClInclude Include="$(SolutionDir)\..\third_party\nanopb\pb_encode.h" />
-    <ClInclude Include="$(SolutionDir)\..\src\core\ext\lb_policy\common.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\ext\census\aggregation.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\ext\census\census_interface.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\ext\census\census_rpc_stats.h" />
@@ -469,6 +469,8 @@
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\lib\iomgr\pollset_windows.c">
     </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\iomgr\pops.c">
+    </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\lib\iomgr\resolve_address_posix.c">
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\lib\iomgr\resolve_address_windows.c">
@@ -677,8 +679,6 @@
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\third_party\nanopb\pb_encode.c">
     </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\src\core\ext\lb_policy\common.c">
-    </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\ext\lb_policy\pick_first\pick_first.c">
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\ext\lb_policy\round_robin\round_robin.c">
diff --git a/vsprojects/vcxproj/grpc_unsecure/grpc_unsecure.vcxproj.filters b/vsprojects/vcxproj/grpc_unsecure/grpc_unsecure.vcxproj.filters
index e2de5acd037a7ed9502d8eba5f1eabe558439d71..ba3b2be04624094d492e54c7995c55883359d98e 100644
--- a/vsprojects/vcxproj/grpc_unsecure/grpc_unsecure.vcxproj.filters
+++ b/vsprojects/vcxproj/grpc_unsecure/grpc_unsecure.vcxproj.filters
@@ -88,6 +88,9 @@
     <ClCompile Include="$(SolutionDir)\..\src\core\lib\iomgr\pollset_windows.c">
       <Filter>src\core\lib\iomgr</Filter>
     </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\iomgr\pops.c">
+      <Filter>src\core\lib\iomgr</Filter>
+    </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\lib\iomgr\resolve_address_posix.c">
       <Filter>src\core\lib\iomgr</Filter>
     </ClCompile>
@@ -400,9 +403,6 @@
     <ClCompile Include="$(SolutionDir)\..\third_party\nanopb\pb_encode.c">
       <Filter>third_party\nanopb</Filter>
     </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\src\core\ext\lb_policy\common.c">
-      <Filter>src\core\ext\lb_policy</Filter>
-    </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\ext\lb_policy\pick_first\pick_first.c">
       <Filter>src\core\ext\lb_policy\pick_first</Filter>
     </ClCompile>
@@ -608,6 +608,9 @@
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\iomgr\pollset_windows.h">
       <Filter>src\core\lib\iomgr</Filter>
     </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\iomgr\pops.h">
+      <Filter>src\core\lib\iomgr</Filter>
+    </ClInclude>
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\iomgr\resolve_address.h">
       <Filter>src\core\lib\iomgr</Filter>
     </ClInclude>
@@ -869,9 +872,6 @@
     <ClInclude Include="$(SolutionDir)\..\third_party\nanopb\pb_encode.h">
       <Filter>third_party\nanopb</Filter>
     </ClInclude>
-    <ClInclude Include="$(SolutionDir)\..\src\core\ext\lb_policy\common.h">
-      <Filter>src\core\ext\lb_policy</Filter>
-    </ClInclude>
     <ClInclude Include="$(SolutionDir)\..\src\core\ext\census\aggregation.h">
       <Filter>src\core\ext\census</Filter>
     </ClInclude>