diff --git a/.gitignore b/.gitignore
index 63332d1b16bf9c1c72fccf477a1e77875d6c0919..002e3e661cbd18bc614b4a7333bef3884217d21e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,6 +4,9 @@ gens
 libs
 objs
 
+# Python virtual environment (pre-3.4 only)
+python2.7_virtual_environment
+
 # gcov coverage data
 coverage
 *.gcno
diff --git a/INSTALL b/INSTALL
index 98c20f589838f58c1e52f05d2ad3302185f20818..48511aff7df9bb9650aa58037c9c5c8b6d272d6a 100644
--- a/INSTALL
+++ b/INSTALL
@@ -17,7 +17,7 @@ A typical unix installation won't require any more steps than running:
 You don't need anything else than GNU Make and gcc. Under a Debian or
 Ubuntu system, this should boil down to the following package:
 
-  # apt-get install build-essential
+  # apt-get install build-essential python-all-dev python-virtualenv
 
 
 *******************************
diff --git a/Makefile b/Makefile
index f88e579fe75fc87037c49a45b49337fda72842c1..1d1aff2a6a6aa9aa5f42c8cb45eff4c96449992e 100644
--- a/Makefile
+++ b/Makefile
@@ -380,6 +380,8 @@ credentials_test: bins/$(CONFIG)/credentials_test
 end2end_test: bins/$(CONFIG)/end2end_test
 interop_client: bins/$(CONFIG)/interop_client
 interop_server: bins/$(CONFIG)/interop_server
+tips_client: bins/$(CONFIG)/tips_client
+tips_client_test: bins/$(CONFIG)/tips_client_test
 qps_client: bins/$(CONFIG)/qps_client
 qps_server: bins/$(CONFIG)/qps_server
 ruby_plugin: bins/$(CONFIG)/ruby_plugin
@@ -559,13 +561,13 @@ privatelibs: privatelibs_c privatelibs_cxx
 
 privatelibs_c:  libs/$(CONFIG)/libgpr_test_util.a libs/$(CONFIG)/libgrpc_test_util.a libs/$(CONFIG)/libend2end_fixture_chttp2_fake_security.a libs/$(CONFIG)/libend2end_fixture_chttp2_fullstack.a libs/$(CONFIG)/libend2end_fixture_chttp2_simple_ssl_fullstack.a libs/$(CONFIG)/libend2end_fixture_chttp2_simple_ssl_with_oauth2_fullstack.a libs/$(CONFIG)/libend2end_fixture_chttp2_socket_pair.a libs/$(CONFIG)/libend2end_fixture_chttp2_socket_pair_one_byte_at_a_time.a libs/$(CONFIG)/libend2end_test_cancel_after_accept.a libs/$(CONFIG)/libend2end_test_cancel_after_accept_and_writes_closed.a libs/$(CONFIG)/libend2end_test_cancel_after_invoke.a libs/$(CONFIG)/libend2end_test_cancel_before_invoke.a libs/$(CONFIG)/libend2end_test_cancel_in_a_vacuum.a libs/$(CONFIG)/libend2end_test_census_simple_request.a libs/$(CONFIG)/libend2end_test_disappearing_server.a libs/$(CONFIG)/libend2end_test_early_server_shutdown_finishes_inflight_calls.a libs/$(CONFIG)/libend2end_test_early_server_shutdown_finishes_tags.a libs/$(CONFIG)/libend2end_test_graceful_server_shutdown.a libs/$(CONFIG)/libend2end_test_invoke_large_request.a libs/$(CONFIG)/libend2end_test_max_concurrent_streams.a libs/$(CONFIG)/libend2end_test_no_op.a libs/$(CONFIG)/libend2end_test_ping_pong_streaming.a libs/$(CONFIG)/libend2end_test_request_response_with_binary_metadata_and_payload.a libs/$(CONFIG)/libend2end_test_request_response_with_metadata_and_payload.a libs/$(CONFIG)/libend2end_test_request_response_with_payload.a libs/$(CONFIG)/libend2end_test_request_response_with_trailing_metadata_and_payload.a libs/$(CONFIG)/libend2end_test_simple_delayed_request.a libs/$(CONFIG)/libend2end_test_simple_request.a libs/$(CONFIG)/libend2end_test_thread_stress.a libs/$(CONFIG)/libend2end_test_writes_done_hangs_with_pending_read.a libs/$(CONFIG)/libend2end_certs.a
 
-privatelibs_cxx:  libs/$(CONFIG)/libgrpc++_test_util.a
+privatelibs_cxx:  libs/$(CONFIG)/libgrpc++_test_util.a libs/$(CONFIG)/libtips_client_lib.a
 
 buildtests: buildtests_c buildtests_cxx
 
 buildtests_c: privatelibs_c bins/$(CONFIG)/alarm_heap_test bins/$(CONFIG)/alarm_list_test bins/$(CONFIG)/alarm_test bins/$(CONFIG)/alpn_test bins/$(CONFIG)/bin_encoder_test bins/$(CONFIG)/census_hash_table_test bins/$(CONFIG)/census_statistics_multiple_writers_circular_buffer_test bins/$(CONFIG)/census_statistics_multiple_writers_test bins/$(CONFIG)/census_statistics_performance_test bins/$(CONFIG)/census_statistics_quick_test bins/$(CONFIG)/census_statistics_small_log_test bins/$(CONFIG)/census_stub_test bins/$(CONFIG)/census_window_stats_test bins/$(CONFIG)/chttp2_status_conversion_test bins/$(CONFIG)/chttp2_stream_encoder_test bins/$(CONFIG)/chttp2_stream_map_test bins/$(CONFIG)/chttp2_transport_end2end_test bins/$(CONFIG)/dualstack_socket_test bins/$(CONFIG)/echo_client bins/$(CONFIG)/echo_server bins/$(CONFIG)/echo_test bins/$(CONFIG)/fd_posix_test bins/$(CONFIG)/fling_client bins/$(CONFIG)/fling_server bins/$(CONFIG)/fling_stream_test bins/$(CONFIG)/fling_test bins/$(CONFIG)/gpr_cancellable_test bins/$(CONFIG)/gpr_cmdline_test bins/$(CONFIG)/gpr_histogram_test bins/$(CONFIG)/gpr_host_port_test bins/$(CONFIG)/gpr_log_test bins/$(CONFIG)/gpr_slice_buffer_test bins/$(CONFIG)/gpr_slice_test bins/$(CONFIG)/gpr_string_test bins/$(CONFIG)/gpr_sync_test bins/$(CONFIG)/gpr_thd_test bins/$(CONFIG)/gpr_time_test bins/$(CONFIG)/gpr_useful_test bins/$(CONFIG)/grpc_base64_test bins/$(CONFIG)/grpc_byte_buffer_reader_test bins/$(CONFIG)/grpc_channel_stack_test bins/$(CONFIG)/grpc_completion_queue_test bins/$(CONFIG)/grpc_credentials_test bins/$(CONFIG)/grpc_json_token_test bins/$(CONFIG)/grpc_stream_op_test bins/$(CONFIG)/hpack_parser_test bins/$(CONFIG)/hpack_table_test bins/$(CONFIG)/httpcli_format_request_test bins/$(CONFIG)/httpcli_parser_test bins/$(CONFIG)/httpcli_test bins/$(CONFIG)/lame_client_test bins/$(CONFIG)/message_compress_test bins/$(CONFIG)/metadata_buffer_test bins/$(CONFIG)/murmur_hash_test bins/$(CONFIG)/no_server_test bins/$(CONFIG)/poll_kick_test bins/$(CONFIG)/resolve_address_test bins/$(CONFIG)/secure_endpoint_test bins/$(CONFIG)/sockaddr_utils_test bins/$(CONFIG)/tcp_client_posix_test bins/$(CONFIG)/tcp_posix_test bins/$(CONFIG)/tcp_server_posix_test bins/$(CONFIG)/time_averaged_stats_test bins/$(CONFIG)/time_test bins/$(CONFIG)/timeout_encoding_test bins/$(CONFIG)/transport_metadata_test bins/$(CONFIG)/chttp2_fake_security_cancel_after_accept_test bins/$(CONFIG)/chttp2_fake_security_cancel_after_accept_and_writes_closed_test bins/$(CONFIG)/chttp2_fake_security_cancel_after_invoke_test bins/$(CONFIG)/chttp2_fake_security_cancel_before_invoke_test bins/$(CONFIG)/chttp2_fake_security_cancel_in_a_vacuum_test bins/$(CONFIG)/chttp2_fake_security_census_simple_request_test bins/$(CONFIG)/chttp2_fake_security_disappearing_server_test bins/$(CONFIG)/chttp2_fake_security_early_server_shutdown_finishes_inflight_calls_test bins/$(CONFIG)/chttp2_fake_security_early_server_shutdown_finishes_tags_test bins/$(CONFIG)/chttp2_fake_security_graceful_server_shutdown_test bins/$(CONFIG)/chttp2_fake_security_invoke_large_request_test bins/$(CONFIG)/chttp2_fake_security_max_concurrent_streams_test bins/$(CONFIG)/chttp2_fake_security_no_op_test bins/$(CONFIG)/chttp2_fake_security_ping_pong_streaming_test bins/$(CONFIG)/chttp2_fake_security_request_response_with_binary_metadata_and_payload_test bins/$(CONFIG)/chttp2_fake_security_request_response_with_metadata_and_payload_test bins/$(CONFIG)/chttp2_fake_security_request_response_with_payload_test bins/$(CONFIG)/chttp2_fake_security_request_response_with_trailing_metadata_and_payload_test bins/$(CONFIG)/chttp2_fake_security_simple_delayed_request_test bins/$(CONFIG)/chttp2_fake_security_simple_request_test bins/$(CONFIG)/chttp2_fake_security_thread_stress_test bins/$(CONFIG)/chttp2_fake_security_writes_done_hangs_with_pending_read_test bins/$(CONFIG)/chttp2_fullstack_cancel_after_accept_test bins/$(CONFIG)/chttp2_fullstack_cancel_after_accept_and_writes_closed_test bins/$(CONFIG)/chttp2_fullstack_cancel_after_invoke_test bins/$(CONFIG)/chttp2_fullstack_cancel_before_invoke_test bins/$(CONFIG)/chttp2_fullstack_cancel_in_a_vacuum_test bins/$(CONFIG)/chttp2_fullstack_census_simple_request_test bins/$(CONFIG)/chttp2_fullstack_disappearing_server_test bins/$(CONFIG)/chttp2_fullstack_early_server_shutdown_finishes_inflight_calls_test bins/$(CONFIG)/chttp2_fullstack_early_server_shutdown_finishes_tags_test bins/$(CONFIG)/chttp2_fullstack_graceful_server_shutdown_test bins/$(CONFIG)/chttp2_fullstack_invoke_large_request_test bins/$(CONFIG)/chttp2_fullstack_max_concurrent_streams_test bins/$(CONFIG)/chttp2_fullstack_no_op_test bins/$(CONFIG)/chttp2_fullstack_ping_pong_streaming_test bins/$(CONFIG)/chttp2_fullstack_request_response_with_binary_metadata_and_payload_test bins/$(CONFIG)/chttp2_fullstack_request_response_with_metadata_and_payload_test bins/$(CONFIG)/chttp2_fullstack_request_response_with_payload_test bins/$(CONFIG)/chttp2_fullstack_request_response_with_trailing_metadata_and_payload_test bins/$(CONFIG)/chttp2_fullstack_simple_delayed_request_test bins/$(CONFIG)/chttp2_fullstack_simple_request_test bins/$(CONFIG)/chttp2_fullstack_thread_stress_test bins/$(CONFIG)/chttp2_fullstack_writes_done_hangs_with_pending_read_test bins/$(CONFIG)/chttp2_simple_ssl_fullstack_cancel_after_accept_test bins/$(CONFIG)/chttp2_simple_ssl_fullstack_cancel_after_accept_and_writes_closed_test bins/$(CONFIG)/chttp2_simple_ssl_fullstack_cancel_after_invoke_test bins/$(CONFIG)/chttp2_simple_ssl_fullstack_cancel_before_invoke_test bins/$(CONFIG)/chttp2_simple_ssl_fullstack_cancel_in_a_vacuum_test bins/$(CONFIG)/chttp2_simple_ssl_fullstack_census_simple_request_test bins/$(CONFIG)/chttp2_simple_ssl_fullstack_disappearing_server_test bins/$(CONFIG)/chttp2_simple_ssl_fullstack_early_server_shutdown_finishes_inflight_calls_test bins/$(CONFIG)/chttp2_simple_ssl_fullstack_early_server_shutdown_finishes_tags_test bins/$(CONFIG)/chttp2_simple_ssl_fullstack_graceful_server_shutdown_test bins/$(CONFIG)/chttp2_simple_ssl_fullstack_invoke_large_request_test bins/$(CONFIG)/chttp2_simple_ssl_fullstack_max_concurrent_streams_test bins/$(CONFIG)/chttp2_simple_ssl_fullstack_no_op_test bins/$(CONFIG)/chttp2_simple_ssl_fullstack_ping_pong_streaming_test bins/$(CONFIG)/chttp2_simple_ssl_fullstack_request_response_with_binary_metadata_and_payload_test bins/$(CONFIG)/chttp2_simple_ssl_fullstack_request_response_with_metadata_and_payload_test bins/$(CONFIG)/chttp2_simple_ssl_fullstack_request_response_with_payload_test bins/$(CONFIG)/chttp2_simple_ssl_fullstack_request_response_with_trailing_metadata_and_payload_test bins/$(CONFIG)/chttp2_simple_ssl_fullstack_simple_delayed_request_test bins/$(CONFIG)/chttp2_simple_ssl_fullstack_simple_request_test bins/$(CONFIG)/chttp2_simple_ssl_fullstack_thread_stress_test bins/$(CONFIG)/chttp2_simple_ssl_fullstack_writes_done_hangs_with_pending_read_test bins/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_cancel_after_accept_test bins/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_cancel_after_accept_and_writes_closed_test bins/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_cancel_after_invoke_test bins/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_cancel_before_invoke_test bins/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_cancel_in_a_vacuum_test bins/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_census_simple_request_test bins/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_disappearing_server_test bins/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_early_server_shutdown_finishes_inflight_calls_test bins/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_early_server_shutdown_finishes_tags_test bins/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_graceful_server_shutdown_test bins/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_invoke_large_request_test bins/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_max_concurrent_streams_test bins/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_no_op_test bins/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_ping_pong_streaming_test bins/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_request_response_with_binary_metadata_and_payload_test bins/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_request_response_with_metadata_and_payload_test bins/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_request_response_with_payload_test bins/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_request_response_with_trailing_metadata_and_payload_test bins/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_simple_delayed_request_test bins/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_simple_request_test bins/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_thread_stress_test bins/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_writes_done_hangs_with_pending_read_test bins/$(CONFIG)/chttp2_socket_pair_cancel_after_accept_test bins/$(CONFIG)/chttp2_socket_pair_cancel_after_accept_and_writes_closed_test bins/$(CONFIG)/chttp2_socket_pair_cancel_after_invoke_test bins/$(CONFIG)/chttp2_socket_pair_cancel_before_invoke_test bins/$(CONFIG)/chttp2_socket_pair_cancel_in_a_vacuum_test bins/$(CONFIG)/chttp2_socket_pair_census_simple_request_test bins/$(CONFIG)/chttp2_socket_pair_disappearing_server_test bins/$(CONFIG)/chttp2_socket_pair_early_server_shutdown_finishes_inflight_calls_test bins/$(CONFIG)/chttp2_socket_pair_early_server_shutdown_finishes_tags_test bins/$(CONFIG)/chttp2_socket_pair_graceful_server_shutdown_test bins/$(CONFIG)/chttp2_socket_pair_invoke_large_request_test bins/$(CONFIG)/chttp2_socket_pair_max_concurrent_streams_test bins/$(CONFIG)/chttp2_socket_pair_no_op_test bins/$(CONFIG)/chttp2_socket_pair_ping_pong_streaming_test bins/$(CONFIG)/chttp2_socket_pair_request_response_with_binary_metadata_and_payload_test bins/$(CONFIG)/chttp2_socket_pair_request_response_with_metadata_and_payload_test bins/$(CONFIG)/chttp2_socket_pair_request_response_with_payload_test bins/$(CONFIG)/chttp2_socket_pair_request_response_with_trailing_metadata_and_payload_test bins/$(CONFIG)/chttp2_socket_pair_simple_delayed_request_test bins/$(CONFIG)/chttp2_socket_pair_simple_request_test bins/$(CONFIG)/chttp2_socket_pair_thread_stress_test bins/$(CONFIG)/chttp2_socket_pair_writes_done_hangs_with_pending_read_test bins/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_cancel_after_accept_test bins/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_cancel_after_accept_and_writes_closed_test bins/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_cancel_after_invoke_test bins/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_cancel_before_invoke_test bins/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_cancel_in_a_vacuum_test bins/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_census_simple_request_test bins/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_disappearing_server_test bins/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_early_server_shutdown_finishes_inflight_calls_test bins/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_early_server_shutdown_finishes_tags_test bins/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_graceful_server_shutdown_test bins/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_invoke_large_request_test bins/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_max_concurrent_streams_test bins/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_no_op_test bins/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_ping_pong_streaming_test bins/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_request_response_with_binary_metadata_and_payload_test bins/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_request_response_with_metadata_and_payload_test bins/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_request_response_with_payload_test bins/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_request_response_with_trailing_metadata_and_payload_test bins/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_simple_delayed_request_test bins/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_simple_request_test bins/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_thread_stress_test bins/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_writes_done_hangs_with_pending_read_test
 
-buildtests_cxx: privatelibs_cxx bins/$(CONFIG)/channel_arguments_test bins/$(CONFIG)/credentials_test bins/$(CONFIG)/end2end_test bins/$(CONFIG)/interop_client bins/$(CONFIG)/interop_server bins/$(CONFIG)/qps_client bins/$(CONFIG)/qps_server bins/$(CONFIG)/status_test bins/$(CONFIG)/sync_client_async_server_test bins/$(CONFIG)/thread_pool_test
+buildtests_cxx: privatelibs_cxx bins/$(CONFIG)/channel_arguments_test bins/$(CONFIG)/credentials_test bins/$(CONFIG)/end2end_test bins/$(CONFIG)/interop_client bins/$(CONFIG)/interop_server bins/$(CONFIG)/tips_client bins/$(CONFIG)/tips_client_test bins/$(CONFIG)/qps_client bins/$(CONFIG)/qps_server bins/$(CONFIG)/status_test bins/$(CONFIG)/sync_client_async_server_test bins/$(CONFIG)/thread_pool_test
 
 test: test_c test_cxx
 
@@ -967,6 +969,8 @@ test_cxx: buildtests_cxx
 	$(Q) ./bins/$(CONFIG)/credentials_test || ( echo test credentials_test failed ; exit 1 )
 	$(E) "[RUN]     Testing end2end_test"
 	$(Q) ./bins/$(CONFIG)/end2end_test || ( echo test end2end_test failed ; exit 1 )
+	$(E) "[RUN]     Testing tips_client_test"
+	$(Q) ./bins/$(CONFIG)/tips_client_test || ( echo test tips_client_test failed ; exit 1 )
 	$(E) "[RUN]     Testing qps_client"
 	$(Q) ./bins/$(CONFIG)/qps_client || ( echo test qps_client failed ; exit 1 )
 	$(E) "[RUN]     Testing qps_server"
@@ -1020,6 +1024,21 @@ strip-shared_cxx: shared_cxx
 	$(E) "[STRIP]   Stripping libgrpc++.so"
 	$(Q) $(STRIP) libs/$(CONFIG)/libgrpc++.$(SHARED_EXT)
 
+gens/examples/tips/empty.pb.cc: examples/tips/empty.proto $(PROTOC_PLUGINS)
+	$(E) "[PROTOC]  Generating protobuf CC file from $<"
+	$(Q) mkdir -p `dirname $@`
+	$(Q) $(PROTOC) --cpp_out=gens --grpc_out=gens --plugin=protoc-gen-grpc=bins/$(CONFIG)/cpp_plugin $<
+
+gens/examples/tips/label.pb.cc: examples/tips/label.proto $(PROTOC_PLUGINS)
+	$(E) "[PROTOC]  Generating protobuf CC file from $<"
+	$(Q) mkdir -p `dirname $@`
+	$(Q) $(PROTOC) --cpp_out=gens --grpc_out=gens --plugin=protoc-gen-grpc=bins/$(CONFIG)/cpp_plugin $<
+
+gens/examples/tips/pubsub.pb.cc: examples/tips/pubsub.proto $(PROTOC_PLUGINS)
+	$(E) "[PROTOC]  Generating protobuf CC file from $<"
+	$(Q) mkdir -p `dirname $@`
+	$(Q) $(PROTOC) --cpp_out=gens --grpc_out=gens --plugin=protoc-gen-grpc=bins/$(CONFIG)/cpp_plugin $<
+
 gens/test/cpp/interop/empty.pb.cc: test/cpp/interop/empty.proto $(PROTOC_PLUGINS)
 	$(E) "[PROTOC]  Generating protobuf CC file from $<"
 	$(Q) mkdir -p `dirname $@`
@@ -1376,6 +1395,7 @@ LIBGRPC_SRC = \
     src/core/iomgr/pollset_kick_posix.c \
     src/core/iomgr/pollset_multipoller_with_poll_posix.c \
     src/core/iomgr/pollset_posix.c \
+    src/core/iomgr/pollset_windows.c \
     src/core/iomgr/resolve_address_posix.c \
     src/core/iomgr/sockaddr_utils.c \
     src/core/iomgr/socket_utils_common_posix.c \
@@ -1494,6 +1514,7 @@ src/core/iomgr/iomgr_posix.c: $(OPENSSL_DEP)
 src/core/iomgr/pollset_kick_posix.c: $(OPENSSL_DEP)
 src/core/iomgr/pollset_multipoller_with_poll_posix.c: $(OPENSSL_DEP)
 src/core/iomgr/pollset_posix.c: $(OPENSSL_DEP)
+src/core/iomgr/pollset_windows.c: $(OPENSSL_DEP)
 src/core/iomgr/resolve_address_posix.c: $(OPENSSL_DEP)
 src/core/iomgr/sockaddr_utils.c: $(OPENSSL_DEP)
 src/core/iomgr/socket_utils_common_posix.c: $(OPENSSL_DEP)
@@ -1633,6 +1654,7 @@ objs/$(CONFIG)/src/core/iomgr/iomgr_posix.o:
 objs/$(CONFIG)/src/core/iomgr/pollset_kick_posix.o: 
 objs/$(CONFIG)/src/core/iomgr/pollset_multipoller_with_poll_posix.o: 
 objs/$(CONFIG)/src/core/iomgr/pollset_posix.o: 
+objs/$(CONFIG)/src/core/iomgr/pollset_windows.o: 
 objs/$(CONFIG)/src/core/iomgr/resolve_address_posix.o: 
 objs/$(CONFIG)/src/core/iomgr/sockaddr_utils.o: 
 objs/$(CONFIG)/src/core/iomgr/socket_utils_common_posix.o: 
@@ -1792,6 +1814,7 @@ LIBGRPC_UNSECURE_SRC = \
     src/core/iomgr/pollset_kick_posix.c \
     src/core/iomgr/pollset_multipoller_with_poll_posix.c \
     src/core/iomgr/pollset_posix.c \
+    src/core/iomgr/pollset_windows.c \
     src/core/iomgr/resolve_address_posix.c \
     src/core/iomgr/sockaddr_utils.c \
     src/core/iomgr/socket_utils_common_posix.c \
@@ -1914,6 +1937,7 @@ objs/$(CONFIG)/src/core/iomgr/iomgr_posix.o:
 objs/$(CONFIG)/src/core/iomgr/pollset_kick_posix.o: 
 objs/$(CONFIG)/src/core/iomgr/pollset_multipoller_with_poll_posix.o: 
 objs/$(CONFIG)/src/core/iomgr/pollset_posix.o: 
+objs/$(CONFIG)/src/core/iomgr/pollset_windows.o: 
 objs/$(CONFIG)/src/core/iomgr/resolve_address_posix.o: 
 objs/$(CONFIG)/src/core/iomgr/sockaddr_utils.o: 
 objs/$(CONFIG)/src/core/iomgr/socket_utils_common_posix.o: 
@@ -2163,6 +2187,58 @@ objs/$(CONFIG)/test/cpp/end2end/async_test_server.o:     gens/test/cpp/util/echo
 objs/$(CONFIG)/test/cpp/util/create_test_channel.o:     gens/test/cpp/util/echo.pb.cc    gens/test/cpp/util/echo_duplicate.pb.cc    gens/test/cpp/util/messages.pb.cc
 
 
+LIBTIPS_CLIENT_LIB_SRC = \
+    gens/examples/tips/label.pb.cc \
+    gens/examples/tips/empty.pb.cc \
+    gens/examples/tips/pubsub.pb.cc \
+    examples/tips/client.cc \
+
+
+LIBTIPS_CLIENT_LIB_OBJS = $(addprefix objs/$(CONFIG)/, $(addsuffix .o, $(basename $(LIBTIPS_CLIENT_LIB_SRC))))
+
+ifeq ($(NO_SECURE),true)
+
+# You can't build secure libraries if you don't have OpenSSL with ALPN.
+
+libs/$(CONFIG)/libtips_client_lib.a: openssl_dep_error
+
+
+else
+
+ifneq ($(OPENSSL_DEP),)
+examples/tips/label.proto: $(OPENSSL_DEP)
+examples/tips/empty.proto: $(OPENSSL_DEP)
+examples/tips/pubsub.proto: $(OPENSSL_DEP)
+examples/tips/client.cc: $(OPENSSL_DEP)
+endif
+
+libs/$(CONFIG)/libtips_client_lib.a: $(ZLIB_DEP) $(OPENSSL_DEP) $(LIBTIPS_CLIENT_LIB_OBJS)
+	$(E) "[AR]      Creating $@"
+	$(Q) mkdir -p `dirname $@`
+	$(Q) rm -f libs/$(CONFIG)/libtips_client_lib.a
+	$(Q) $(AR) rcs libs/$(CONFIG)/libtips_client_lib.a $(LIBTIPS_CLIENT_LIB_OBJS)
+ifeq ($(SYSTEM),Darwin)
+	$(Q) ranlib libs/$(CONFIG)/libtips_client_lib.a 
+endif
+
+
+
+
+
+endif
+
+ifneq ($(NO_SECURE),true)
+ifneq ($(NO_DEPS),true)
+-include $(LIBTIPS_CLIENT_LIB_OBJS:.o=.dep)
+endif
+endif
+
+
+
+
+objs/$(CONFIG)/examples/tips/client.o:     gens/examples/tips/label.pb.cc    gens/examples/tips/empty.pb.cc    gens/examples/tips/pubsub.pb.cc
+
+
 LIBEND2END_FIXTURE_CHTTP2_FAKE_SECURITY_SRC = \
     test/core/end2end/fixtures/chttp2_fake_security.c \
 
@@ -5469,6 +5545,68 @@ endif
 endif
 
 
+TIPS_CLIENT_SRC = \
+    examples/tips/client_main.cc \
+
+TIPS_CLIENT_OBJS = $(addprefix objs/$(CONFIG)/, $(addsuffix .o, $(basename $(TIPS_CLIENT_SRC))))
+
+ifeq ($(NO_SECURE),true)
+
+# You can't build secure targets if you don't have OpenSSL with ALPN.
+
+bins/$(CONFIG)/tips_client: openssl_dep_error
+
+else
+
+bins/$(CONFIG)/tips_client: $(TIPS_CLIENT_OBJS) libs/$(CONFIG)/libtips_client_lib.a libs/$(CONFIG)/libgrpc++_test_util.a libs/$(CONFIG)/libgrpc_test_util.a libs/$(CONFIG)/libgrpc++.a libs/$(CONFIG)/libgrpc.a libs/$(CONFIG)/libgpr_test_util.a libs/$(CONFIG)/libgpr.a
+	$(E) "[LD]      Linking $@"
+	$(Q) mkdir -p `dirname $@`
+	$(Q) $(LDXX) $(LDFLAGS) $(TIPS_CLIENT_OBJS) $(GTEST_LIB) libs/$(CONFIG)/libtips_client_lib.a libs/$(CONFIG)/libgrpc++_test_util.a libs/$(CONFIG)/libgrpc_test_util.a libs/$(CONFIG)/libgrpc++.a libs/$(CONFIG)/libgrpc.a libs/$(CONFIG)/libgpr_test_util.a libs/$(CONFIG)/libgpr.a $(LDLIBSXX) $(LDLIBS) $(LDLIBS_SECURE) -o bins/$(CONFIG)/tips_client
+
+endif
+
+objs/$(CONFIG)/examples/tips/client_main.o:  libs/$(CONFIG)/libtips_client_lib.a libs/$(CONFIG)/libgrpc++_test_util.a libs/$(CONFIG)/libgrpc_test_util.a libs/$(CONFIG)/libgrpc++.a libs/$(CONFIG)/libgrpc.a libs/$(CONFIG)/libgpr_test_util.a libs/$(CONFIG)/libgpr.a
+
+deps_tips_client: $(TIPS_CLIENT_OBJS:.o=.dep)
+
+ifneq ($(NO_SECURE),true)
+ifneq ($(NO_DEPS),true)
+-include $(TIPS_CLIENT_OBJS:.o=.dep)
+endif
+endif
+
+
+TIPS_CLIENT_TEST_SRC = \
+    examples/tips/client_test.cc \
+
+TIPS_CLIENT_TEST_OBJS = $(addprefix objs/$(CONFIG)/, $(addsuffix .o, $(basename $(TIPS_CLIENT_TEST_SRC))))
+
+ifeq ($(NO_SECURE),true)
+
+# You can't build secure targets if you don't have OpenSSL with ALPN.
+
+bins/$(CONFIG)/tips_client_test: openssl_dep_error
+
+else
+
+bins/$(CONFIG)/tips_client_test: $(TIPS_CLIENT_TEST_OBJS) libs/$(CONFIG)/libtips_client_lib.a libs/$(CONFIG)/libgrpc++_test_util.a libs/$(CONFIG)/libgrpc_test_util.a libs/$(CONFIG)/libgrpc++.a libs/$(CONFIG)/libgrpc.a libs/$(CONFIG)/libgpr_test_util.a libs/$(CONFIG)/libgpr.a
+	$(E) "[LD]      Linking $@"
+	$(Q) mkdir -p `dirname $@`
+	$(Q) $(LDXX) $(LDFLAGS) $(TIPS_CLIENT_TEST_OBJS) $(GTEST_LIB) libs/$(CONFIG)/libtips_client_lib.a libs/$(CONFIG)/libgrpc++_test_util.a libs/$(CONFIG)/libgrpc_test_util.a libs/$(CONFIG)/libgrpc++.a libs/$(CONFIG)/libgrpc.a libs/$(CONFIG)/libgpr_test_util.a libs/$(CONFIG)/libgpr.a $(LDLIBSXX) $(LDLIBS) $(LDLIBS_SECURE) -o bins/$(CONFIG)/tips_client_test
+
+endif
+
+objs/$(CONFIG)/examples/tips/client_test.o:  libs/$(CONFIG)/libtips_client_lib.a libs/$(CONFIG)/libgrpc++_test_util.a libs/$(CONFIG)/libgrpc_test_util.a libs/$(CONFIG)/libgrpc++.a libs/$(CONFIG)/libgrpc.a libs/$(CONFIG)/libgpr_test_util.a libs/$(CONFIG)/libgpr.a
+
+deps_tips_client_test: $(TIPS_CLIENT_TEST_OBJS:.o=.dep)
+
+ifneq ($(NO_SECURE),true)
+ifneq ($(NO_DEPS),true)
+-include $(TIPS_CLIENT_TEST_OBJS:.o=.dep)
+endif
+endif
+
+
 QPS_CLIENT_SRC = \
     gens/test/cpp/qps/qpstest.pb.cc \
     test/cpp/qps/client.cc \
diff --git a/build.json b/build.json
index 9a5134ecd6aa9eec133db8e0d7afc447d1c82fff..cacbfe2ae50d1b54911e53aac0343e00cd0319bc 100644
--- a/build.json
+++ b/build.json
@@ -48,7 +48,9 @@
         "src/core/iomgr/pollset.h",
         "src/core/iomgr/pollset_kick.h",
         "src/core/iomgr/pollset_kick_posix.h",
+        "src/core/iomgr/pollset_kick_windows.h",
         "src/core/iomgr/pollset_posix.h",
+        "src/core/iomgr/pollset_windows.h",
         "src/core/iomgr/resolve_address.h",
         "src/core/iomgr/sockaddr.h",
         "src/core/iomgr/sockaddr_posix.h",
@@ -126,6 +128,7 @@
         "src/core/iomgr/pollset_kick_posix.c",
         "src/core/iomgr/pollset_multipoller_with_poll_posix.c",
         "src/core/iomgr/pollset_posix.c",
+        "src/core/iomgr/pollset_windows.c",
         "src/core/iomgr/resolve_address_posix.c",
         "src/core/iomgr/sockaddr_utils.c",
         "src/core/iomgr/socket_utils_common_posix.c",
@@ -408,6 +411,22 @@
         "test/cpp/end2end/async_test_server.cc",
         "test/cpp/util/create_test_channel.cc"
       ]
+    },
+    {
+      "name": "tips_client_lib",
+      "build": "private",
+      "language": "c++",
+      "src": [
+        "examples/tips/label.proto",
+        "examples/tips/empty.proto",
+        "examples/tips/pubsub.proto",
+        "examples/tips/client.cc"
+      ],
+      "deps": [
+        "grpc++",
+        "grpc",
+        "gpr"
+      ]
     }
   ],
   "targets": [
@@ -1493,6 +1512,41 @@
       ],
       "run": false
     },
+    {
+      "name": "tips_client",
+      "build": "test",
+      "run": false,
+      "language": "c++",
+      "src": [
+        "examples/tips/client_main.cc"
+      ],
+      "deps": [
+        "tips_client_lib",
+        "grpc++_test_util",
+        "grpc_test_util",
+        "grpc++",
+        "grpc",
+        "gpr_test_util",
+        "gpr"
+      ]
+    },
+    {
+      "name": "tips_client_test",
+      "build": "test",
+      "language": "c++",
+      "src": [
+        "examples/tips/client_test.cc"
+      ],
+      "deps": [
+        "tips_client_lib",
+        "grpc++_test_util",
+        "grpc_test_util",
+        "grpc++",
+        "grpc",
+        "gpr_test_util",
+        "gpr"
+      ]
+    },
     {
       "name": "qps_client",
       "build": "test",
diff --git a/examples/tips/client.cc b/examples/tips/client.cc
new file mode 100644
index 0000000000000000000000000000000000000000..695ff80e8afacaf490a6d6e734567a3b2a17cb1b
--- /dev/null
+++ b/examples/tips/client.cc
@@ -0,0 +1,60 @@
+/*
+ *
+ * Copyright 2014, 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++/client_context.h>
+
+#include "examples/tips/client.h"
+
+using tech::pubsub::Topic;
+using tech::pubsub::PublisherService;
+
+namespace grpc {
+namespace examples {
+namespace tips {
+
+Client::Client(std::shared_ptr<ChannelInterface> channel)
+    : stub_(PublisherService::NewStub(channel)) {
+}
+
+Status Client::CreateTopic(grpc::string topic) {
+  Topic request;
+  Topic response;
+  request.set_name(topic);
+  ClientContext context;
+
+  return stub_->CreateTopic(&context, request, &response);
+}
+
+}  // namespace tips
+}  // namespace examples
+}  // namespace grpc
diff --git a/examples/tips/client.h b/examples/tips/client.h
new file mode 100644
index 0000000000000000000000000000000000000000..3f4f1fd836990a328b0f16e3cf38f4d4f86d2f2e
--- /dev/null
+++ b/examples/tips/client.h
@@ -0,0 +1,54 @@
+/*
+ *
+ * Copyright 2014, 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++/channel_interface.h>
+#include <grpc++/status.h>
+
+#include "examples/tips/pubsub.pb.h"
+
+namespace grpc {
+namespace examples {
+namespace tips {
+
+class Client {
+ public:
+  Client(std::shared_ptr<grpc::ChannelInterface> channel);
+  Status CreateTopic(grpc::string topic);
+
+ private:
+  std::unique_ptr<tech::pubsub::PublisherService::Stub> stub_;
+};
+
+}  // namespace tips
+}  // namespace examples
+}  // namespace grpc
diff --git a/examples/tips/client_main.cc b/examples/tips/client_main.cc
new file mode 100644
index 0000000000000000000000000000000000000000..17567b6f17a5f4f3e0db822ac4e8a690ad1860b9
--- /dev/null
+++ b/examples/tips/client_main.cc
@@ -0,0 +1,73 @@
+/*
+ *
+ * Copyright 2014, 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/grpc.h>
+#include <grpc/support/log.h>
+#include <google/gflags.h>
+#include <grpc++/channel_interface.h>
+#include <grpc++/create_channel.h>
+#include <grpc++/status.h>
+
+#include "examples/tips/client.h"
+#include "test/cpp/util/create_test_channel.h"
+
+DEFINE_bool(enable_ssl, true, "Whether to use ssl/tls.");
+DEFINE_bool(use_prod_roots, true, "True to use SSL roots for production GFE");
+DEFINE_int32(server_port, 0, "Server port.");
+DEFINE_string(server_host, "127.0.0.1", "Server host to connect to");
+DEFINE_string(server_host_override, "foo.test.google.com",
+              "Override the server host which is sent in HTTP header");
+
+int main(int argc, char** argv) {
+  grpc_init();
+  google::ParseCommandLineFlags(&argc, &argv, true);
+  gpr_log(GPR_INFO, "Start TIPS client");
+
+  GPR_ASSERT(FLAGS_server_port);
+  const int host_port_buf_size = 1024;
+  char host_port[host_port_buf_size];
+  snprintf(host_port, host_port_buf_size, "%s:%d", FLAGS_server_host.c_str(),
+           FLAGS_server_port);
+
+  std::shared_ptr<grpc::ChannelInterface> channel(
+      grpc::CreateTestChannel(host_port, FLAGS_server_host_override,
+                              FLAGS_enable_ssl, FLAGS_use_prod_roots));
+
+  grpc::examples::tips::Client client(channel);
+  grpc::Status s = client.CreateTopic("test");
+  GPR_ASSERT(s.IsOk());
+
+  channel.reset();
+  grpc_shutdown();
+  return 0;
+}
diff --git a/examples/tips/client_test.cc b/examples/tips/client_test.cc
new file mode 100644
index 0000000000000000000000000000000000000000..69238f2c6fcc060eadb098f136899ebdd9deb95a
--- /dev/null
+++ b/examples/tips/client_test.cc
@@ -0,0 +1,106 @@
+/*
+ *
+ * Copyright 2014, 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++/channel_arguments.h>
+#include <grpc++/channel_interface.h>
+#include <grpc++/client_context.h>
+#include <grpc++/create_channel.h>
+#include <grpc++/server.h>
+#include <grpc++/server_builder.h>
+#include <grpc++/server_context.h>
+#include <grpc++/status.h>
+#include <gtest/gtest.h>
+
+#include "examples/tips/client.h"
+#include "test/core/util/port.h"
+#include "test/core/util/test_config.h"
+
+using grpc::ChannelInterface;
+
+namespace grpc {
+namespace testing {
+namespace {
+
+const char kTopic[] = "test topic";
+
+class PublishServiceImpl : public tech::pubsub::PublisherService::Service {
+ public:
+  Status CreateTopic(::grpc::ServerContext* context,
+                     const ::tech::pubsub::Topic* request,
+                     ::tech::pubsub::Topic* response) override {
+    EXPECT_EQ(request->name(), kTopic);
+    return Status::OK;
+  }
+};
+
+class End2endTest : public ::testing::Test {
+ protected:
+  void SetUp() override {
+    int port = grpc_pick_unused_port_or_die();
+    server_address_ << "localhost:" << port;
+    // Setup server
+    ServerBuilder builder;
+    builder.AddPort(server_address_.str());
+    builder.RegisterService(service_.service());
+    server_ = builder.BuildAndStart();
+
+    channel_ = CreateChannel(server_address_.str(), ChannelArguments());
+  }
+
+  void TearDown() override { server_->Shutdown(); }
+
+  std::unique_ptr<Server> server_;
+  std::ostringstream server_address_;
+  PublishServiceImpl service_;
+
+  std::shared_ptr<ChannelInterface> channel_;
+};
+
+TEST_F(End2endTest, CreateTopic) {
+  grpc::examples::tips::Client client(channel_);
+  client.CreateTopic(kTopic);
+}
+
+}  // namespace
+}  // namespace testing
+}  // namespace grpc
+
+int main(int argc, char** argv) {
+  grpc_test_init(argc, argv);
+  grpc_init();
+  ::testing::InitGoogleTest(&argc, argv);
+  gpr_log(GPR_INFO, "Start test ...");
+  int result = RUN_ALL_TESTS();
+  grpc_shutdown();
+  return result;
+}
diff --git a/examples/tips/empty.proto b/examples/tips/empty.proto
new file mode 100644
index 0000000000000000000000000000000000000000..adf66b5e614dc51bf180205a4374a79271ad0235
--- /dev/null
+++ b/examples/tips/empty.proto
@@ -0,0 +1,13 @@
+syntax = "proto2";
+
+package proto2;
+
+// An empty message that you can re-use to avoid defining duplicated empty
+// messages in your project. A typical example is to use it as argument or the
+// return value of a service API. For instance:
+//
+//   service Foo {
+//     rpc Bar (proto2.Empty) returns (proto2.Empty) { };
+//   };
+//
+message Empty {}
diff --git a/examples/tips/label.proto b/examples/tips/label.proto
new file mode 100644
index 0000000000000000000000000000000000000000..e93ac9dea3006c76644dedfb4f71c0857ac6b14e
--- /dev/null
+++ b/examples/tips/label.proto
@@ -0,0 +1,48 @@
+// Labels provide a way to associate user-defined metadata with various
+// objects.  Labels may be used to organize objects into non-hierarchical
+// groups; think metadata tags attached to mp3s.
+
+syntax = "proto2";
+
+package tech.label;
+
+// A key-value pair applied to a given object.
+message Label {
+  // The key of a label is a syntactically valid URL (as per RFC 1738) with
+  // the "scheme" and initial slashes omitted and with the additional
+  // restrictions noted below.  Each key should be globally unique.  The
+  // "host" portion is called the "namespace" and is not necessarily
+  // resolvable to a network endpoint.  Instead, the namespace indicates what
+  // system or entity defines the semantics of the label.  Namespaces do not
+  // restrict the set of objects to which a label may be associated.
+  //
+  // Keys are defined by the following grammar:
+  //
+  //   key          = hostname "/" kpath
+  //   kpath        = ksegment *[ "/" ksegment ]
+  //   ksegment     = alphadigit | *[ alphadigit | "-" | "_" | "." ]
+  //
+  // where "hostname" and "alphadigit" are defined as in RFC 1738.
+  //
+  // Example key:
+  //   spanner.google.com/universe
+  required string key = 1;
+
+  // The value of the label.
+  oneof value {
+    // A string value.
+    string str_value = 2;
+    // An integer value.
+    int64 num_value = 3;
+  }
+}
+
+// A collection of labels, such as the set of all labels attached to an
+// object.  Each label in the set must have a different key.
+//
+// Users should prefer to embed "repeated Label" directly when possible.
+// This message should only be used in cases where that isn't possible (e.g.
+// with oneof).
+message Labels {
+  repeated Label label = 1;
+}
diff --git a/examples/tips/pubsub.proto b/examples/tips/pubsub.proto
new file mode 100644
index 0000000000000000000000000000000000000000..0b3bd5d012a4bffe50f30d4a2e41f4b0d751ba56
--- /dev/null
+++ b/examples/tips/pubsub.proto
@@ -0,0 +1,702 @@
+// Specification of the Pubsub API.
+
+syntax = "proto2";
+
+import "examples/tips/empty.proto";
+import "examples/tips/label.proto";
+
+package tech.pubsub;
+
+// -----------------------------------------------------------------------------
+// Overview of the Pubsub API
+// -----------------------------------------------------------------------------
+
+// This file describes an API for a Pubsub system.  This system provides a
+// reliable many-to-many communication mechanism between independently written
+// publishers and subscribers where the publisher publishes messages to "topics"
+// and each subscriber creates a "subscription" and consumes messages from it.
+//
+// (a) The pubsub system maintains bindings between topics and subscriptions.
+// (b) A publisher publishes messages into a topic.
+// (c) The pubsub system delivers messages from topics into relevant
+//     subscriptions.
+// (d) A subscriber receives pending messages from its subscription and
+//     acknowledges or nacks each one to the pubsub system.
+// (e) The pubsub system removes acknowledged messages from that subscription.
+
+// -----------------------------------------------------------------------------
+// Data Model
+// -----------------------------------------------------------------------------
+
+// The data model consists of the following:
+//
+// * Topic: A topic is a resource to which messages are published by publishers.
+//     Topics are named, and the name of the topic is unique within the pubsub
+//     system.
+//
+// * Subscription: A subscription records the subscriber's interest in a topic.
+//     It can optionally include a query to select a subset of interesting
+//     messages.  The pubsub system maintains a logical cursor tracking the
+//     matching messages which still need to be delivered and acked so that
+//     they can retried as needed.  The set of messages that have not been
+//     acknowledged is called the subscription backlog.
+//
+// * Message: A message is a unit of data that flows in the system.  It contains
+//     opaque data from the publisher along with its labels.
+//
+// * Message Labels (optional): A set of opaque key, value pairs assigned
+//     by the publisher which the subscriber can use for filtering out messages
+//     in the topic.  For example, a label with key "foo.com/device_type" and
+//     value "mobile" may be added for messages that are only relevant for a
+//     mobile subscriber; a subscriber on a phone may decide to create a
+//     subscription only for messages that have this label.
+
+// -----------------------------------------------------------------------------
+// Publisher Flow
+// -----------------------------------------------------------------------------
+
+// A publisher publishes messages to the topic using the Publish request:
+//
+//   PubsubMessage message;
+//   message.set_data("....");
+//   Label label;
+//   label.set_key("foo.com/key1");
+//   label.set_str_value("value1");
+//   message.add_label(label);
+//   PublishRequest request;
+//   request.set_topic("topicName");
+//   request.set_message(message);
+//   PublisherService.Publish(request);
+
+// -----------------------------------------------------------------------------
+// Subscriber Flow
+// -----------------------------------------------------------------------------
+
+// The subscriber part of the API is richer than the publisher part and has a
+// number of concepts w.r.t. subscription creation and monitoring:
+//
+// (1) A subscriber creates a subscription using the CreateSubscription call.
+//     It may specify an optional "query" to indicate that it wants to receive
+//     only messages with a certain set of labels using the label query syntax.
+//     It may also specify an optional truncation policy to indicate when old
+//     messages from the subcription can be removed.
+//
+// (2) A subscriber receives messages in one of two ways: via push or pull.
+//
+// (a) To receive messages via push, the PushConfig field must be specified in
+//     the Subscription parameter when creating a subscription.  The PushConfig
+//     specifies an endpoint at which the subscriber must expose the
+//     PushEndpointService.  Messages are received via the HandlePubsubEvent
+//     method.  The push subscriber responds to the HandlePubsubEvent method
+//     with a result code that indicates one of three things: Ack (the message
+//     has been successfully processed and the Pubsub system may delete it),
+//     Nack (the message has been rejected, the Pubsub system should resend it
+//     at a later time), or Push-Back (this is a Nack with the additional
+//     semantics that the subscriber is overloaded and the pubsub system should
+//     back off on the rate at which it is invoking HandlePubsubEvent).  The
+//     endpoint may be a load balancer for better scalability.
+//
+// (b) To receive messages via pull a subscriber calls the Pull method on the
+//     SubscriberService to get messages from the subscription.  For each
+//     individual message, the subscriber may use the ack_id received in the
+//     PullResponse to Ack the message, Nack the message, or modify the ack
+//     deadline with ModifyAckDeadline.  See the
+//     Subscription.ack_deadline_seconds field documentation for details on the
+//     ack deadline behavior.
+//
+//     Note: Messages may be consumed in parallel by multiple subscribers making
+//       Pull calls to the same subscription; this will result in the set of
+//       messages from the subscription being shared and each subscriber
+//       receiving a subset of the messages.
+//
+// (4) The subscriber can explicitly truncate the current subscription.
+//
+// (5) "Truncated" events are delivered when a subscription is
+//     truncated, whether due to the subscription's truncation policy
+//     or an explicit request from the subscriber.
+//
+// Subscription creation:
+//
+//   Subscription subscription;
+//   subscription.set_topic("topicName");
+//   subscription.set_name("subscriptionName");
+//   subscription.push_config().set_push_endpoint("machinename:8888");
+//   SubscriberService.CreateSubscription(subscription);
+//
+// Consuming messages via push:
+//
+//  TODO(eschapira): Add HTTP push example.
+//
+//  The port 'machinename:8888' must be bound to a stubby server that implements
+//  the PushEndpointService with the following method:
+//
+//   int HandlePubsubEvent(PubsubEvent event) {
+//     if (event.subscription().equals("subscriptionName")) {
+//       if (event.has_message()) {
+//         Process(event.message().data());
+//       } else if (event.truncated()) {
+//         ProcessTruncatedEvent();
+//       }
+//     }
+//     return OK;  // This return code implies an acknowledgment
+//   }
+//
+// Consuming messages via pull:
+//
+//  The subscription must be created without setting the push_config field.
+//
+//   PullRequest pull_request;
+//   pull_request.set_subscription("subscriptionName");
+//   pull_request.set_return_immediately(false);
+//   while (true) {
+//     PullResponse pull_response;
+//     if (SubscriberService.Pull(pull_request, pull_response) == OK) {
+//       PubsubEvent event = pull_response.pubsub_event();
+//       if (event.has_message()) {
+//         Process(event.message().data());
+//       } else if (event.truncated()) {
+//         ProcessTruncatedEvent();
+//       }
+//       AcknowledgeRequest ack_request;
+//       ackRequest.set_subscription("subscriptionName");
+//       ackRequest.set_ack_id(pull_response.ack_id());
+//       SubscriberService.Acknowledge(ack_request);
+//     }
+//   }
+
+// -----------------------------------------------------------------------------
+// Reliability Semantics
+// -----------------------------------------------------------------------------
+
+// When a subscriber successfully creates a subscription using
+// Subscriber.CreateSubscription, it establishes a "subscription point" with
+// respect to that subscription - the subscriber is guaranteed to receive any
+// message published after this subscription point that matches the
+// subscription's query.  Note that messages published before the Subscription
+// point may or may not be delivered.
+//
+// If the system truncates the subscription according to the specified
+// truncation policy, the system delivers a subscription status event with the
+// "truncated" field set to true.  We refer to such events as "truncation
+// events".  A truncation event:
+//
+// * Informs the subscriber that part of the subscription messages have been
+//   discarded.  The subscriber may want to recover from the message loss, e.g.,
+//   by resyncing its state with its backend.
+// * Establishes a new subscription point, i.e., the subscriber is guaranteed to
+//   receive all changes published after the trunction event is received (or
+//   until another truncation event is received).
+//
+// Note that messages are not delivered in any particular order by the pubsub
+// system.  Furthermore, the system guarantees at-least-once delivery
+// of each message or truncation events until acked.
+
+// -----------------------------------------------------------------------------
+// Deletion
+// -----------------------------------------------------------------------------
+
+// Both topics and subscriptions may be deleted.  Deletion of a topic implies
+// deletion of all attached subscriptions.
+//
+// When a subscription is deleted directly by calling DeleteSubscription, all
+// messages are immediately dropped.  If it is a pull subscriber, future pull
+// requests will return NOT_FOUND.
+//
+// When a topic is deleted all corresponding subscriptions are immediately
+// deleted, and subscribers experience the same behavior as directly deleting
+// the subscription.
+
+// -----------------------------------------------------------------------------
+// The Publisher service and its protos.
+// -----------------------------------------------------------------------------
+
+// The service that an application uses to manipulate topics, and to send
+// messages to a topic.
+service PublisherService {
+
+  // Creates the given topic with the given name.
+  rpc CreateTopic(Topic) returns (Topic) {
+  }
+
+  // Adds a message to the topic.  Returns NOT_FOUND if the topic does not
+  // exist.
+  // (-- For different error code values returned via Stubby, see
+  // util/task/codes.proto. --)
+  rpc Publish(PublishRequest) returns (proto2.Empty) {
+  }
+
+  // Adds one or more messages to the topic. Returns NOT_FOUND if the topic does
+  // not exist.
+  rpc PublishBatch(PublishBatchRequest) returns (PublishBatchResponse) {
+  }
+
+  // Gets the configuration of a topic. Since the topic only has the name
+  // attribute, this method is only useful to check the existence of a topic.
+  // If other attributes are added in the future, they will be returned here.
+  rpc GetTopic(GetTopicRequest) returns (Topic) {
+  }
+
+  // Lists matching topics.
+  rpc ListTopics(ListTopicsRequest) returns (ListTopicsResponse) {
+  }
+
+  // Deletes the topic with the given name.  All subscriptions to this topic
+  // are also deleted. Returns NOT_FOUND if the topic does not exist.
+  // After a topic is deleted, a new topic may be created with the same name.
+  rpc DeleteTopic(DeleteTopicRequest) returns (proto2.Empty)  {
+  }
+}
+
+// A topic resource.
+message Topic {
+  // Name of the topic.
+  optional string name = 1;
+}
+
+// A message data and its labels.
+message PubsubMessage {
+  // The message payload.
+  optional bytes data = 1;
+
+  // Optional list of labels for this message. Keys in this collection must
+  // be unique.
+  //(-- TODO(eschapira): Define how key namespace may be scoped to the topic.--)
+  repeated tech.label.Label label = 2;
+
+  // ID of this message assigned by the server at publication time. Guaranteed
+  // to be unique within the topic. This value may be read by a subscriber
+  // that receives a PubsubMessage via a Pull call or a push delivery. It must
+  // not be populated by a publisher in a Publish call.
+  optional string message_id = 3;
+}
+
+// Request for the GetTopic method.
+message GetTopicRequest {
+  // The name of the topic to get.
+  optional string topic = 1;
+}
+
+// Request for the Publish method.
+message PublishRequest {
+  // The message in the request will be published on this topic.
+  optional string topic = 1;
+
+  // The message to publish.
+  optional PubsubMessage message = 2;
+}
+
+// Request for the PublishBatch method.
+message PublishBatchRequest {
+  // The messages in the request will be published on this topic.
+  optional string topic = 1;
+
+  // The messages to publish.
+  repeated PubsubMessage messages = 2;
+}
+
+// Response for the PublishBatch method.
+message PublishBatchResponse {
+  // The server-assigned ID of each published message, in the same order as
+  // the messages in the request. IDs are guaranteed to be unique within
+  // the topic.
+  repeated string message_ids = 1;
+}
+
+// Request for the ListTopics method.
+message ListTopicsRequest {
+  // A valid label query expression.
+  // (-- Which labels are required or supported is implementation-specific. --)
+  optional string query = 1;
+
+  // Maximum number of topics to return.
+  // (-- If not specified or <= 0, the implementation will select a reasonable
+  // value. --)
+  optional int32 max_results = 2;
+
+  // The value obtained in the last <code>ListTopicsResponse</code>
+  // for continuation.
+  optional string page_token = 3;
+
+}
+
+// Response for the ListTopics method.
+message ListTopicsResponse {
+  // The resulting topics.
+  repeated Topic topic = 1;
+
+  // If not empty, indicates that there are more topics that match the request,
+  // and this value should be passed to the next <code>ListTopicsRequest</code>
+  // to continue.
+  optional string next_page_token = 2;
+}
+
+// Request for the Delete method.
+message DeleteTopicRequest {
+  // Name of the topic to delete.
+  optional string topic = 1;
+}
+
+// -----------------------------------------------------------------------------
+// The Subscriber service and its protos.
+// -----------------------------------------------------------------------------
+
+// The service that an application uses to manipulate subscriptions and to
+// consume messages from a subscription via the pull method.
+service SubscriberService {
+
+  // Creates a subscription on a given topic for a given subscriber.
+  // If the subscription already exists, returns ALREADY_EXISTS.
+  // If the corresponding topic doesn't exist, returns NOT_FOUND.
+  //
+  // If the name is not provided in the request, the server will assign a random
+  // name for this subscription on the same project as the topic.
+  rpc CreateSubscription(Subscription) returns (Subscription) {
+  }
+
+  // Gets the configuration details of a subscription.
+  rpc GetSubscription(GetSubscriptionRequest) returns (Subscription) {
+  }
+
+  // Lists matching subscriptions.
+  rpc ListSubscriptions(ListSubscriptionsRequest)
+      returns (ListSubscriptionsResponse) {
+  }
+
+  // Deletes an existing subscription. All pending messages in the subscription
+  // are immediately dropped. Calls to Pull after deletion will return
+  // NOT_FOUND.
+  rpc DeleteSubscription(DeleteSubscriptionRequest) returns (proto2.Empty) {
+  }
+
+  // Removes all the pending messages in the subscription and releases the
+  // storage associated with them. Results in a truncation event to be sent to
+  // the subscriber. Messages added after this call returns are stored in the
+  // subscription as before.
+  rpc TruncateSubscription(TruncateSubscriptionRequest) returns (proto2.Empty) {
+  }
+
+  //
+  // Push subscriber calls.
+  //
+
+  // Modifies the <code>PushConfig</code> for a specified subscription.
+  // This method can be used to suspend the flow of messages to an endpoint
+  // by clearing the <code>PushConfig</code> field in the request. Messages
+  // will be accumulated for delivery even if no push configuration is
+  // defined or while the configuration is modified.
+  rpc ModifyPushConfig(ModifyPushConfigRequest) returns (proto2.Empty) {
+  }
+
+  //
+  // Pull Subscriber calls
+  //
+
+  // Pulls a single message from the server.
+  // If return_immediately is true, and no messages are available in the
+  // subscription, this method returns FAILED_PRECONDITION. The system is free
+  // to return an UNAVAILABLE error if no messages are available in a
+  // reasonable amount of time (to reduce system load).
+  rpc Pull(PullRequest) returns (PullResponse) {
+  }
+
+  // Pulls messages from the server. Returns an empty list if there are no
+  // messages available in the backlog. The system is free to return UNAVAILABLE
+  // if there are too many pull requests outstanding for the given subscription.
+  rpc PullBatch(PullBatchRequest) returns (PullBatchResponse) {
+  }
+
+  // Modifies the Ack deadline for a message received from a pull request.
+  rpc ModifyAckDeadline(ModifyAckDeadlineRequest) returns (proto2.Empty) {
+  }
+
+  // Acknowledges a particular received message: the Pub/Sub system can remove
+  // the given message from the subscription. Acknowledging a message whose
+  // Ack deadline has expired may succeed, but the message could have been
+  // already redelivered. Acknowledging a message more than once will not
+  // result in an error. This is only used for messages received via pull.
+  rpc Acknowledge(AcknowledgeRequest) returns (proto2.Empty) {
+  }
+
+  // Refuses processing a particular received message. The system will
+  // redeliver this message to some consumer of the subscription at some
+  // future time. This is only used for messages received via pull.
+  rpc Nack(NackRequest) returns (proto2.Empty) {
+  }
+}
+
+// A subscription resource.
+message Subscription {
+  // Name of the subscription.
+  optional string name = 1;
+
+  // The name of the topic from which this subscription is receiving messages.
+  optional string topic = 2;
+
+  // If <code>query</code> is non-empty, only messages on the subscriber's
+  // topic whose labels match the query will be returned. Otherwise all
+  // messages on the topic will be returned.
+  // (-- The query syntax is described in tech/label/proto/label_query.proto --)
+  optional string query = 3;
+
+  // The subscriber may specify requirements for truncating unacknowledged
+  // subscription entries. The system will honor the
+  // <code>CreateSubscription</code> request only if it can meet these
+  // requirements. If this field is not specified, messages are never truncated
+  // by the system.
+  optional TruncationPolicy truncation_policy = 4;
+
+  // Specifies which messages can be truncated by the system.
+  message TruncationPolicy {
+    oneof policy {
+      // If <code>max_bytes</code> is specified, the system is allowed to drop
+      // old messages to keep the combined size of stored messages under
+      // <code>max_bytes</code>. This is a hint; the system may keep more than
+      // this many bytes, but will make a best effort to keep the size from
+      // growing much beyond this parameter.
+      int64 max_bytes = 1;
+
+      // If <code>max_age_seconds</code> is specified, the system is allowed to
+      // drop messages that have been stored for at least this many seconds.
+      // This is a hint; the system may keep these messages, but will make a
+      // best effort to remove them when their maximum age is reached.
+      int64 max_age_seconds = 2;
+    }
+  }
+
+  // If push delivery is used with this subscription, this field is
+  // used to configure it.
+  optional PushConfig push_config = 5;
+
+  // For either push or pull delivery, the value is the maximum time after a
+  // subscriber receives a message before the subscriber should acknowledge or
+  // Nack the message. If the Ack deadline for a message passes without an
+  // Ack or a Nack, the Pub/Sub system will eventually redeliver the message.
+  // If a subscriber acknowledges after the deadline, the Pub/Sub system may
+  // accept the Ack, but it is possible that the message has been already
+  // delivered again. Multiple Acks to the message are allowed and will
+  // succeed.
+  //
+  // For push delivery, this value is used to set the request timeout for
+  // the call to the push endpoint.
+  //
+  // For pull delivery, this value is used as the initial value for the Ack
+  // deadline. It may be overridden for a specific pull request (message) with
+  // <code>ModifyAckDeadline</code>.
+  // While a message is outstanding (i.e. it has been delivered to a pull
+  // subscriber and the subscriber has not yet Acked or Nacked), the Pub/Sub
+  // system will not deliver that message to another pull subscriber
+  // (on a best-effort basis).
+  optional int32 ack_deadline_seconds = 6;
+
+  // If this parameter is set to n, the system is allowed to (but not required
+  // to) delete the subscription when at least n seconds have elapsed since the
+  // client presence was detected. (Presence is detected through any
+  // interaction using the subscription ID, including Pull(), Get(), or
+  // acknowledging a message.)
+  //
+  // If this parameter is not set, the subscription will stay live until
+  // explicitly deleted.
+  //
+  // Clients can detect such garbage collection when a Get call or a Pull call
+  // (for pull subscribers only) returns NOT_FOUND.
+  optional int64 garbage_collect_seconds = 7;
+}
+
+// Configuration for a push delivery endpoint.
+message PushConfig {
+  // A URL locating the endpoint to which messages should be pushed.
+  // For example, a Webhook endpoint might use "https://example.com/push".
+  // (-- An Android application might use "gcm:<REGID>", where <REGID> is a
+  // GCM registration id allocated for pushing messages to the application. --)
+  optional string push_endpoint = 1;
+}
+
+// An event indicating a received message or truncation event.
+message PubsubEvent {
+  // The subscription that received the event.
+  optional string subscription = 1;
+
+  oneof type {
+    // A received message.
+    PubsubMessage message = 2;
+
+    // Indicates that this subscription has been truncated.
+    bool truncated = 3;
+
+    // Indicates that this subscription has been deleted. (Note that pull
+    // subscribers will always receive NOT_FOUND in response in their pull
+    // request on the subscription, rather than seeing this boolean.)
+    bool deleted = 4;
+  }
+}
+
+// Request for the GetSubscription method.
+message GetSubscriptionRequest {
+  // The name of the subscription to get.
+  optional string subscription = 1;
+}
+
+// Request for the ListSubscriptions method.
+message ListSubscriptionsRequest {
+  // A valid label query expression.
+  // (-- Which labels are required or supported is implementation-specific.
+  // TODO(eschapira): This method must support to query by topic. We must
+  // define the key URI for the "topic" label. --)
+  optional string query = 1;
+
+  // Maximum number of subscriptions to return.
+  // (-- If not specified or <= 0, the implementation will select a reasonable
+  // value. --)
+  optional int32 max_results = 3;
+
+  // The value obtained in the last <code>ListSubscriptionsResponse</code>
+  // for continuation.
+  optional string page_token = 4;
+}
+
+// Response for the ListSubscriptions method.
+message ListSubscriptionsResponse {
+  // The subscriptions that match the request.
+  repeated Subscription subscription = 1;
+
+  // If not empty, indicates that there are more subscriptions that match the
+  // request and this value should be passed to the next
+  // <code>ListSubscriptionsRequest</code> to continue.
+  optional string next_page_token = 2;
+}
+
+// Request for the TruncateSubscription method.
+message TruncateSubscriptionRequest {
+  // The subscription that is being truncated.
+  optional string subscription = 1;
+}
+
+// Request for the DeleteSubscription method.
+message DeleteSubscriptionRequest {
+  // The subscription to delete.
+  optional string subscription = 1;
+}
+
+// Request for the ModifyPushConfig method.
+message ModifyPushConfigRequest {
+  // The name of the subscription.
+  optional string subscription = 1;
+
+  // An empty <code>push_config</code> indicates that the Pub/Sub system should
+  // pause pushing messages from the given subscription.
+  optional PushConfig push_config = 2;
+}
+
+// -----------------------------------------------------------------------------
+// The protos used by a pull subscriber.
+// -----------------------------------------------------------------------------
+
+// Request for the Pull method.
+message PullRequest {
+  // The subscription from which a message should be pulled.
+  optional string subscription = 1;
+
+  // If this is specified as true the system will respond immediately even if
+  // it is not able to return a message in the Pull response. Otherwise the
+  // system is allowed to wait until at least one message is available rather
+  // than returning FAILED_PRECONDITION. The client may cancel the request if
+  // it does not wish to wait any longer for the response.
+  optional bool return_immediately = 2;
+}
+
+// Either a <code>PubsubMessage</code> or a truncation event. One of these two
+// must be populated.
+message PullResponse {
+  // This ID must be used to acknowledge the received event or message.
+  optional string ack_id = 1;
+
+  // A pubsub message or truncation event.
+  optional PubsubEvent pubsub_event = 2;
+}
+
+// Request for the PullBatch method.
+message PullBatchRequest {
+  // The subscription from which messages should be pulled.
+  optional string subscription = 1;
+
+  // If this is specified as true the system will respond immediately even if
+  // it is not able to return a message in the Pull response. Otherwise the
+  // system is allowed to wait until at least one message is available rather
+  // than returning no messages. The client may cancel the request if it does
+  // not wish to wait any longer for the response.
+  optional bool return_immediately = 2;
+
+  // The maximum number of PubsubEvents returned for this request. The Pub/Sub
+  // system may return fewer than the number of events specified.
+  optional int32 max_events = 3;
+}
+
+// Response for the PullBatch method.
+message PullBatchResponse {
+
+  // Received Pub/Sub messages or status events. The Pub/Sub system will return
+  // zero messages if there are no more messages available in the backlog. The
+  // Pub/Sub system may return fewer than the max_events requested even if
+  // there are more messages available in the backlog.
+  repeated PullResponse pull_responses = 2;
+}
+
+// Request for the ModifyAckDeadline method.
+message ModifyAckDeadlineRequest {
+  // The name of the subscription from which messages are being pulled.
+  optional string subscription = 1;
+
+  // The acknowledgment ID.
+  optional string ack_id = 2;
+
+  // The new Ack deadline. Must be >= 0.
+  optional int32 ack_deadline_seconds = 3;
+}
+
+// Request for the Acknowledge method.
+message AcknowledgeRequest {
+  // The subscription whose message is being acknowledged.
+  optional string subscription = 1;
+
+  // The acknowledgment ID for the message being acknowledged. This was
+  // returned by the Pub/Sub system in the Pull response.
+  repeated string ack_id = 2;
+}
+
+// Request for the Nack method.
+message NackRequest {
+  // The subscription whose message is being Nacked.
+  optional string subscription = 1;
+
+  // The acknowledgment ID for the message being refused. This was returned by
+  // the Pub/Sub system in the Pull response.
+  repeated string ack_id = 2;
+}
+
+// -----------------------------------------------------------------------------
+// The service and protos used by a push subscriber.
+// -----------------------------------------------------------------------------
+
+// The service that a subscriber uses to handle messages sent via push
+// delivery.
+// This service is not currently exported for HTTP clients.
+// TODO(eschapira): Explain HTTP subscribers.
+service PushEndpointService {
+  // Sends a <code>PubsubMessage</code> or a subscription status event to a
+  // push endpoint.
+  // The push endpoint responds with an empty message and a code from
+  // util/task/codes.proto. The following codes have a particular meaning to the
+  // Pub/Sub system:
+  // OK          - This is interpreted by Pub/Sub as Ack.
+  // ABORTED     - This is intepreted by Pub/Sub as a Nack, without implying
+  //               pushback for congestion control.  The Pub/Sub system will
+  //               retry this message at a later time.
+  // UNAVAILABLE - This is intepreted by Pub/Sub as a Nack, with the additional
+  //               semantics of push-back.  The Pub/Sub system will use an AIMD
+  //               congestion control algorithm to backoff the rate of sending
+  //               messages from this subscription.
+  // Any other code, or a failure to respond, will be interpreted in the same
+  // way as ABORTED; i.e. the system will retry the message at a later time to
+  // ensure reliable delivery.
+  rpc HandlePubsubEvent(PubsubEvent) returns (proto2.Empty);
+}
diff --git a/include/grpc/support/port_platform.h b/include/grpc/support/port_platform.h
index 05a5bbe1bc9e0160a287e0c5bd60978513560e42..118a919aee4a4c14cd28253a68141972a64b0982 100644
--- a/include/grpc/support/port_platform.h
+++ b/include/grpc/support/port_platform.h
@@ -132,6 +132,14 @@
 #error Must define exactly one of GPR_CPU_LINUX, GPR_CPU_POSIX, GPR_WIN32
 #endif
 
+#if defined(GPR_POSIX_MULTIPOLL_WITH_POLL) && !defined(GPR_POSIX_SOCKET)
+#error Must define GPR_POSIX_SOCKET to use GPR_POSIX_MULTIPOLL_WITH_POLL
+#endif
+
+#if defined(GPR_POSIX_SOCKET) + defined(GPR_WIN32) != 1
+#error Must define exactly one of GPR_POSIX_POLLSET, GPR_WIN32
+#endif
+
 typedef int16_t gpr_int16;
 typedef int32_t gpr_int32;
 typedef int64_t gpr_int64;
diff --git a/src/core/iomgr/endpoint_pair_posix.c b/src/core/iomgr/endpoint_pair_posix.c
index f08d1344ebb07543fe0ecfd800c2474e52c2f34f..3f53402cf3c5ffd6144ded9d9f9b9dabe02ebefc 100644
--- a/src/core/iomgr/endpoint_pair_posix.c
+++ b/src/core/iomgr/endpoint_pair_posix.c
@@ -31,6 +31,10 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
+#ifdef GPR_POSIX_SOCKET
+
 #include "src/core/iomgr/endpoint_pair.h"
 
 #include <errno.h>
@@ -59,3 +63,5 @@ grpc_endpoint_pair grpc_iomgr_create_endpoint_pair(size_t read_slice_size) {
   p.server = grpc_tcp_create(grpc_fd_create(sv[0]), read_slice_size);
   return p;
 }
+
+#endif
diff --git a/src/core/iomgr/fd_posix.c b/src/core/iomgr/fd_posix.c
index 3cd2f9a8e0ca9a3bc9ccd130dfc9bfe1f3cd5e25..9f70a26c64357f6617ce863a79c17ca86ef25532 100644
--- a/src/core/iomgr/fd_posix.c
+++ b/src/core/iomgr/fd_posix.c
@@ -31,6 +31,10 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
+#ifdef GPR_POSIX_SOCKET
+
 #include "src/core/iomgr/fd_posix.h"
 
 #include <assert.h>
@@ -272,3 +276,5 @@ void grpc_fd_become_readable(grpc_fd *fd, int allow_synchronous_callback) {
 void grpc_fd_become_writable(grpc_fd *fd, int allow_synchronous_callback) {
   set_ready(fd, &fd->writest, allow_synchronous_callback);
 }
+
+#endif
diff --git a/src/core/iomgr/pollset.h b/src/core/iomgr/pollset.h
index 36d80d5c2975272928f798d389b2069e4dc1193f..b9fcf45ea689e1503f3fed0165382460a662a2ac 100644
--- a/src/core/iomgr/pollset.h
+++ b/src/core/iomgr/pollset.h
@@ -48,6 +48,10 @@
 #include "src/core/iomgr/pollset_posix.h"
 #endif
 
+#ifdef GPR_WIN32
+#include "src/core/iomgr/pollset_windows.h"
+#endif
+
 void grpc_pollset_init(grpc_pollset *pollset);
 void grpc_pollset_destroy(grpc_pollset *pollset);
 
diff --git a/src/core/iomgr/pollset_kick.h b/src/core/iomgr/pollset_kick.h
index f088818b9a10d06043381dcb3b5bb0d5542df679..02f3e414337a1335c39bf5e4cac4976b7820469f 100644
--- a/src/core/iomgr/pollset_kick.h
+++ b/src/core/iomgr/pollset_kick.h
@@ -41,8 +41,10 @@
 
 #ifdef GPR_POSIX_SOCKET
 #include "src/core/iomgr/pollset_kick_posix.h"
-#else
-#error "No pollset kick support on platform"
+#endif
+
+#ifdef GPR_WIN32
+#include "src/core/iomgr/pollset_kick_windows.h"
 #endif
 
 void grpc_pollset_kick_global_init(void);
diff --git a/src/core/iomgr/pollset_kick_posix.c b/src/core/iomgr/pollset_kick_posix.c
index 9f85b6137afc77d72e6f609ef1de488b072d52d4..4386cf5a464d20894a475a494e476a45f2a6ffdd 100644
--- a/src/core/iomgr/pollset_kick_posix.c
+++ b/src/core/iomgr/pollset_kick_posix.c
@@ -31,6 +31,10 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
+#ifdef GPR_POSIX_SOCKET
+
 #include "src/core/iomgr/pollset_kick_posix.h"
 
 #include <errno.h>
@@ -175,3 +179,5 @@ void grpc_pollset_kick_kick(grpc_pollset_kick_state *kick_state) {
   }
   gpr_mu_unlock(&kick_state->mu);
 }
+
+#endif
diff --git a/src/core/iomgr/pollset_kick_windows.h b/src/core/iomgr/pollset_kick_windows.h
new file mode 100644
index 0000000000000000000000000000000000000000..243e519dad67baea1a53263a6b3ff92f80b967ab
--- /dev/null
+++ b/src/core/iomgr/pollset_kick_windows.h
@@ -0,0 +1,45 @@
+/*
+ *
+ * Copyright 2015, 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.
+ *
+ */
+
+#ifndef __GRPC_INTERNAL_IOMGR_POLLSET_KICK_WINDOWS_H_
+#define __GRPC_INTERNAL_IOMGR_POLLSET_KICK_WINDOWS_H_
+
+#include <grpc/support/sync.h>
+
+struct grpc_kick_pipe_info;
+
+typedef struct grpc_pollset_kick_state {
+  int unused;
+} grpc_pollset_kick_state;
+
+#endif /* __GRPC_INTERNAL_IOMGR_POLLSET_KICK_WINDOWS_H_ */
diff --git a/src/core/iomgr/pollset_posix.c b/src/core/iomgr/pollset_posix.c
index 2555322532c3ba11a08d47ecdb1f8ad1f5c4e405..39e2dc466727fdb7fea8adefa0bfc52a240c31ca 100644
--- a/src/core/iomgr/pollset_posix.c
+++ b/src/core/iomgr/pollset_posix.c
@@ -31,6 +31,10 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
+#ifdef GPR_POSIX_SOCKET
+
 #include "src/core/iomgr/pollset_posix.h"
 
 #include <errno.h>
@@ -288,3 +292,5 @@ static void become_unary_pollset(grpc_pollset *pollset, grpc_fd *fd) {
   pollset->data.ptr = fd;
   grpc_fd_ref(fd);
 }
+
+#endif /* GPR_POSIX_POLLSET */
diff --git a/src/core/iomgr/pollset_windows.c b/src/core/iomgr/pollset_windows.c
new file mode 100644
index 0000000000000000000000000000000000000000..3fb39918b356c73ace1a55749d5033b32527147d
--- /dev/null
+++ b/src/core/iomgr/pollset_windows.c
@@ -0,0 +1,38 @@
+/*
+ *
+ * Copyright 2014, 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/port_platform.h>
+
+#ifdef GPR_WIN32
+
+#endif /* GPR_WIN32 */
diff --git a/src/core/iomgr/pollset_windows.h b/src/core/iomgr/pollset_windows.h
new file mode 100644
index 0000000000000000000000000000000000000000..53b9ffa5abbecd1f83fd3a64150b85808ca760df
--- /dev/null
+++ b/src/core/iomgr/pollset_windows.h
@@ -0,0 +1,54 @@
+/*
+ *
+ * Copyright 2014, 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.
+ *
+ */
+
+#ifndef __GRPC_INTERNAL_IOMGR_POLLSET_WINDOWS_H_
+#define __GRPC_INTERNAL_IOMGR_POLLSET_WINDOWS_H_
+
+#include <grpc/support/sync.h>
+
+#include "src/core/iomgr/pollset_kick.h"
+
+/* forward declare only in this file to avoid leaking impl details via
+   pollset.h; real users of grpc_fd should always include 'fd_posix.h' and not
+   use the struct tag */
+struct grpc_fd;
+
+typedef struct grpc_pollset {
+	gpr_mu mu;
+	gpr_cv cv;
+} grpc_pollset;
+
+#define GRPC_POLLSET_MU(pollset) (&(pollset)->mu)
+#define GRPC_POLLSET_CV(pollset) (&(pollset)->cv)
+
+#endif /* __GRPC_INTERNAL_IOMGR_POLLSET_WINDOWS_H_ */
diff --git a/src/core/iomgr/resolve_address.h b/src/core/iomgr/resolve_address.h
index 37ec0f0335063d57cba48926a72529e8f6b91ae9..7b537b1767470b785bb495dfea55c0eb84af8caf 100644
--- a/src/core/iomgr/resolve_address.h
+++ b/src/core/iomgr/resolve_address.h
@@ -34,10 +34,12 @@
 #ifndef __GRPC_INTERNAL_IOMGR_RESOLVE_ADDRESS_H__
 #define __GRPC_INTERNAL_IOMGR_RESOLVE_ADDRESS_H__
 
-#include <sys/socket.h>
+#include <stddef.h>
+
+#define GRPC_MAX_SOCKADDR_SIZE 128
 
 typedef struct {
-  struct sockaddr_storage addr;
+  char addr[GRPC_MAX_SOCKADDR_SIZE];
   int len;
 } grpc_resolved_address;
 
diff --git a/src/core/iomgr/socket_utils_common_posix.c b/src/core/iomgr/socket_utils_common_posix.c
index bd29e2b5fd2f2fbe1012cfb3c05d80b098b6c9a0..3a0639f35678f86a2ce9d97ca2628aa1c7372986 100644
--- a/src/core/iomgr/socket_utils_common_posix.c
+++ b/src/core/iomgr/socket_utils_common_posix.c
@@ -31,6 +31,10 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
+#ifdef GPR_POSIX_SOCKET
+
 #include "src/core/iomgr/socket_utils_posix.h"
 
 #include <arpa/inet.h>
@@ -187,3 +191,5 @@ int grpc_create_dualstack_socket(const struct sockaddr *addr, int type,
   *dsmode = family == AF_INET ? GRPC_DSMODE_IPV4 : GRPC_DSMODE_NONE;
   return socket(family, type, protocol);
 }
+
+#endif
diff --git a/src/core/iomgr/tcp_client_posix.c b/src/core/iomgr/tcp_client_posix.c
index d675c2dceceafd51d3b34361d6e0a8dd1c703da4..851530ce685c16aa6006d37c776767a8900446d8 100644
--- a/src/core/iomgr/tcp_client_posix.c
+++ b/src/core/iomgr/tcp_client_posix.c
@@ -31,6 +31,10 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
+#ifdef GPR_POSIX_SOCKET
+
 #include "src/core/iomgr/tcp_client.h"
 
 #include <errno.h>
@@ -229,3 +233,5 @@ void grpc_tcp_client_connect(void (*cb)(void *arg, grpc_endpoint *ep),
   grpc_alarm_init(&ac->alarm, deadline, on_alarm, ac, gpr_now());
   grpc_fd_notify_on_write(ac->fd, on_writable, ac);
 }
+
+#endif
diff --git a/src/core/iomgr/tcp_posix.c b/src/core/iomgr/tcp_posix.c
index 657f34aaf9918b7ef5ba626a0dd8e7c3c98bea3e..64996bd07d1bed353e3e8be63475b77b038ba299 100644
--- a/src/core/iomgr/tcp_posix.c
+++ b/src/core/iomgr/tcp_posix.c
@@ -31,6 +31,10 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
+#ifdef GPR_POSIX_SOCKET
+
 #include "src/core/iomgr/tcp_posix.h"
 
 #include <errno.h>
@@ -539,3 +543,5 @@ grpc_endpoint *grpc_tcp_create(grpc_fd *em_fd, size_t slice_size) {
   tcp->em_fd = em_fd;
   return &tcp->base;
 }
+
+#endif
diff --git a/src/core/iomgr/tcp_server.h b/src/core/iomgr/tcp_server.h
index 8ffd7d3569e1a2a064f5659f6e2b541b07a641a7..c4d836e9b53be100f04de19b0d9fc8234651e884 100644
--- a/src/core/iomgr/tcp_server.h
+++ b/src/core/iomgr/tcp_server.h
@@ -34,9 +34,6 @@
 #ifndef __GRPC_INTERNAL_IOMGR_TCP_SERVER_H__
 #define __GRPC_INTERNAL_IOMGR_TCP_SERVER_H__
 
-#include <sys/types.h>
-#include <sys/socket.h>
-
 #include "src/core/iomgr/endpoint.h"
 
 /* Forward decl of grpc_tcp_server */
@@ -63,7 +60,7 @@ void grpc_tcp_server_start(grpc_tcp_server *server, grpc_pollset *pollset,
    For raw access to the underlying sockets, see grpc_tcp_server_get_fd(). */
 /* TODO(ctiller): deprecate this, and make grpc_tcp_server_add_ports to handle
                   all of the multiple socket port matching logic in one place */
-int grpc_tcp_server_add_port(grpc_tcp_server *s, const struct sockaddr *addr,
+int grpc_tcp_server_add_port(grpc_tcp_server *s, const void *addr,
                              int addr_len);
 
 /* Returns the file descriptor of the Nth listening socket on this server,
diff --git a/src/core/iomgr/tcp_server_posix.c b/src/core/iomgr/tcp_server_posix.c
index e6c02186eea6da80f486ccaa9df1816059e41dab..2d6c6a73c2ab8d93df123452d8fbdccf8055b24f 100644
--- a/src/core/iomgr/tcp_server_posix.c
+++ b/src/core/iomgr/tcp_server_posix.c
@@ -31,6 +31,10 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
+#ifdef GPR_POSIX_SOCKET
+
 #define _GNU_SOURCE
 #include "src/core/iomgr/tcp_server.h"
 
@@ -265,7 +269,7 @@ static int add_socket_to_server(grpc_tcp_server *s, int fd,
   return port;
 }
 
-int grpc_tcp_server_add_port(grpc_tcp_server *s, const struct sockaddr *addr,
+int grpc_tcp_server_add_port(grpc_tcp_server *s, const void *addr,
                              int addr_len) {
   int allocated_port1 = -1;
   int allocated_port2 = -1;
@@ -364,3 +368,5 @@ void grpc_tcp_server_start(grpc_tcp_server *s, grpc_pollset *pollset,
   }
   gpr_mu_unlock(&s->mu);
 }
+
+#endif
diff --git a/src/core/transport/chttp2/frame_data.c b/src/core/transport/chttp2/frame_data.c
index c22a22373707dda90a99f3af3a1bc3758b248043..00b020b31b16489d1a32619a187958300a348d75 100644
--- a/src/core/transport/chttp2/frame_data.c
+++ b/src/core/transport/chttp2/frame_data.c
@@ -141,7 +141,7 @@ grpc_chttp2_parse_error grpc_chttp2_data_parser_parse(
                             gpr_slice_sub(slice, cur - beg, end - beg));
         p->state = GRPC_CHTTP2_DATA_FH_0;
         return GRPC_CHTTP2_PARSE_OK;
-      } else if (end - cur > p->frame_size) {
+      } else if ((gpr_uint32)(end - cur) > p->frame_size) {
         state->need_flush_reads = 1;
         grpc_sopb_add_slice(
             &p->incoming_sopb,
diff --git a/src/core/transport/chttp2/hpack_parser.c b/src/core/transport/chttp2/hpack_parser.c
index 07b0f81098c403403454d3fd0b5bca1493427b62..64e08ffac727dd0c0816957e54eddec19a72de9a 100644
--- a/src/core/transport/chttp2/hpack_parser.c
+++ b/src/core/transport/chttp2/hpack_parser.c
@@ -1212,7 +1212,7 @@ static int huff_nibble(grpc_chttp2_hpack_parser *p, gpr_uint8 nibble) {
   gpr_int16 next = next_sub_tbl[16 * next_tbl[p->huff_state] + nibble];
   if (emit != -1) {
     if (emit >= 0 && emit < 256) {
-      gpr_uint8 c = emit;
+      gpr_uint8 c = (gpr_uint8) emit;
       if (!append_string(p, &c, (&c) + 1)) return 0;
     } else {
       assert(emit == 256);
diff --git a/src/core/transport/chttp2_transport.c b/src/core/transport/chttp2_transport.c
index e61afb71ae8d20d908ee4918b2e5bca48d073047..17b37d6d4ae9edee4c002ee4566a9b20c98b0f01 100644
--- a/src/core/transport/chttp2_transport.c
+++ b/src/core/transport/chttp2_transport.c
@@ -1611,7 +1611,7 @@ static int process_read(transport *t, gpr_slice slice) {
         }
         t->deframe_state = DTS_FH_0;
         return 1;
-      } else if (end - cur > t->incoming_frame_size) {
+      } else if ((gpr_uint32)(end - cur) > t->incoming_frame_size) {
         if (!parse_frame_slice(
                 t, gpr_slice_sub_no_ref(slice, cur - beg,
                                         cur + t->incoming_frame_size - beg),
diff --git a/src/core/transport/stream_op.c b/src/core/transport/stream_op.c
index c77c8cde1f1232a0d63a5d5fad36694513a810fa..555543fc4b81c81ba466043e96d52db91c2c90d0 100644
--- a/src/core/transport/stream_op.c
+++ b/src/core/transport/stream_op.c
@@ -63,7 +63,7 @@ void grpc_sopb_reset(grpc_stream_op_buffer *sopb) {
 }
 
 void grpc_stream_ops_unref_owned_objects(grpc_stream_op *ops, size_t nops) {
-  int i;
+  size_t i;
   for (i = 0; i < nops; i++) {
     switch (ops[i].type) {
       case GRPC_OP_SLICE:
diff --git a/src/php/ext/grpc/credentials.c b/src/php/ext/grpc/credentials.c
index c63196bf909ef062f204d152bd9750d12cedb98d..46c825a48fa7aeb14fb7f1e41dc0c6eb90cfe192 100644
--- a/src/php/ext/grpc/credentials.c
+++ b/src/php/ext/grpc/credentials.c
@@ -81,6 +81,8 @@ PHP_METHOD(Credentials, createSsl) {
 
   int root_certs_length, private_key_length = 0, cert_chain_length = 0;
 
+  pem_key_cert_pair.private_key = pem_key_cert_pair.cert_chain = NULL;
+
   /* "s|s!s! == 1 string, 2 optional nullable strings */
   if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|s!s!",
                             &pem_root_certs, &root_certs_length,
diff --git a/src/python/__init__.py b/src/python/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/src/python/_framework/__init__.py b/src/python/_framework/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/src/python/_framework/foundation/__init__.py b/src/python/_framework/foundation/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/src/ruby/lib/grpc/beefcake.rb b/src/python/_framework/foundation/_logging_pool_test.py
similarity index 63%
rename from src/ruby/lib/grpc/beefcake.rb
rename to src/python/_framework/foundation/_logging_pool_test.py
index fd3ebbf4b8436ec0aec353a228fd6dcd2798ec2c..ffe07c788da9a9186e11d353380e724a159594be 100644
--- a/src/ruby/lib/grpc/beefcake.rb
+++ b/src/python/_framework/foundation/_logging_pool_test.py
@@ -1,4 +1,4 @@
-# Copyright 2014, Google Inc.
+# Copyright 2015, Google Inc.
 # All rights reserved.
 #
 # Redistribution and use in source and binary forms, with or without
@@ -27,31 +27,38 @@
 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
-require 'beefcake'
-
-module Beefcake
-  # Re-open the beefcake message module to add a static encode
-  #
-  # This is a temporary measure while beefcake is used as the default proto
-  # library for developing grpc ruby.  Once that changes to the official proto
-  # library this can be removed.  It's necessary to allow the update the service
-  # module to assume a static encode method.
-  # TODO(temiola): remove this.
-  module Message
-    # additional mixin module that adds static encode method when include
-    module StaticEncode
-      # encodes o with its instance#encode method
-      def encode(o)
-        o.encode
-      end
-    end
-
-    # extend self.included in Beefcake::Message to include StaticEncode
-    def self.included(o)
-      o.extend StaticEncode
-      o.extend Dsl
-      o.extend Decode
-      o.send(:include, Encode)
-    end
-  end
-end
+"""Tests for google3.net.rpc.python.framework.foundation.logging_pool."""
+
+import unittest
+
+from _framework.foundation import logging_pool
+
+_POOL_SIZE = 16
+
+
+class LoggingPoolTest(unittest.TestCase):
+
+  def testUpAndDown(self):
+    pool = logging_pool.pool(_POOL_SIZE)
+    pool.shutdown(wait=True)
+
+    with logging_pool.pool(_POOL_SIZE) as pool:
+      self.assertIsNotNone(pool)
+
+  def testTaskExecuted(self):
+    test_list = []
+
+    with logging_pool.pool(_POOL_SIZE) as pool:
+      pool.submit(lambda: test_list.append(object())).result()
+
+    self.assertTrue(test_list)
+
+  def testException(self):
+    with logging_pool.pool(_POOL_SIZE) as pool:
+      raised_exception = pool.submit(lambda: 1/0).exception()
+
+    self.assertIsNotNone(raised_exception)
+
+
+if __name__ == '__main__':
+  unittest.main()
diff --git a/src/python/_framework/foundation/logging_pool.py b/src/python/_framework/foundation/logging_pool.py
new file mode 100644
index 0000000000000000000000000000000000000000..7c7a6eebfca16b62f350d082c3d14672bc74e67d
--- /dev/null
+++ b/src/python/_framework/foundation/logging_pool.py
@@ -0,0 +1,83 @@
+# Copyright 2015, 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.
+
+"""A thread pool that logs exceptions raised by tasks executed within it."""
+
+import functools
+import logging
+
+from concurrent import futures
+
+
+def _wrap(behavior):
+  """Wraps an arbitrary callable behavior in exception-logging."""
+  @functools.wraps(behavior)
+  def _wrapping(*args, **kwargs):
+    try:
+      return behavior(*args, **kwargs)
+    except Exception as e:
+      logging.exception('Unexpected exception from task run in logging pool!')
+      raise
+  return _wrapping
+
+
+class _LoggingPool(object):
+  """An exception-logging futures.ThreadPoolExecutor-compatible thread pool."""
+
+  def __init__(self, backing_pool):
+    self._backing_pool = backing_pool
+
+  def __enter__(self):
+    return self
+
+  def __exit__(self, exc_type, exc_val, exc_tb):
+    self._backing_pool.shutdown(wait=True)
+
+  def submit(self, fn, *args, **kwargs):
+    return self._backing_pool.submit(_wrap(fn), *args, **kwargs)
+
+  def map(self, func, *iterables, **kwargs):
+    return self._backing_pool.map(
+        _wrap(func), *iterables, timeout=kwargs.get('timeout', None))
+
+  def shutdown(self, wait=True):
+    self._backing_pool.shutdown(wait=wait)
+
+
+def pool(max_workers):
+  """Creates a thread pool that logs exceptions raised by the tasks within it.
+
+  Args:
+    max_workers: The maximum number of worker threads to allow the pool.
+
+  Returns:
+    A futures.ThreadPoolExecutor-compatible thread pool that logs exceptions
+      raised by the tasks executed within it.
+  """
+  return _LoggingPool(futures.ThreadPoolExecutor(max_workers))
diff --git a/src/ruby/README.md b/src/ruby/README.md
index 23aec2b20afe1f21dffa7060ebddd8de11fa415e..7f7558dc6770b56d3bc42aee30c03009d464393a 100755
--- a/src/ruby/README.md
+++ b/src/ruby/README.md
@@ -1,64 +1,63 @@
-Ruby for GRPC
-=============
+gRPC Ruby
+=========
 
-LAYOUT
-------
+A Ruby implementation of gRPC, Google's RPC library.
 
-Directory structure is the recommended layout for [ruby extensions](http://guides.rubygems.org/gems-with-extensions/)
 
- * ext: the extension code
- * lib: the entrypoint grpc ruby library to be used in a 'require' statement
- * test: tests
+INSTALLATION PREREQUISITES
+--------------------------
 
+This requires Ruby 2.x, as the rpc api surface uses keyword args.
 
-DEPENDENCIES
-------------
 
+INSTALLING
+----------
 
-* Extension
+- Install the gRPC core library
+TODO: describe this, once the core distribution mechanism is defined.
 
-The extension can be built and tested using
-[rake](https://rubygems.org/gems/rake).  However, the rake-extensiontask rule
-is not supported on older versions of rubygems, and the necessary version of
-rubygems.
+$ gem install grpc
 
-This is resolved by using [RVM](https://rvm.io/) instead; install a single-user
-ruby environment, and develop on the latest stable version of ruby (2.1.5).
 
+Installing from source
+----------------------
 
-INSTALLATION PREREQUISITES
---------------------------
-
-Install RVM
+- Build or Install the gRPC core
+E.g, from the root of the grpc [git repo](https://github.com/google/grpc)
+$ cd ../..
+$ make && sudo make install
 
+- Install Ruby 2.x. Consider doing this with [RVM](http://rvm.io), it's a nice way of controlling
+  the exact ruby version that's used.
 $ command curl -sSL https://rvm.io/mpapis.asc | gpg --import -
 $ \curl -sSL https://get.rvm.io | bash -s stable --ruby
 $
 $ # follow the instructions to ensure that your're using the latest stable version of Ruby
 $ # and that the rvm command is installed
-$
-$ gem install bundler  # install bundler, the standard ruby package manager
 
-HACKING
--------
+- Install [bundler](http://bundler.io/)
+$ gem install bundler
 
-The extension can be built and tested using the Rakefile.
+- Finally, install grpc ruby locally.
+$ cd <install_dir>
+$ bundle install
+$ rake  # compiles the extension, runs the unit tests, see rake -T for other options
 
-$ # create a workspace
-$ git5 start <your-git5-branch> net/grpc
-$
-$ # build the C library and install it in $HOME/grpc_dev
-$ <google3>/net/grpc/c/build_gyp/build_grpc_dev.sh
-$
-$ # build the ruby extension and test it.
-$ cd google3_dir/net/grpc/ruby
-$ rake
 
-Finally, install grpc ruby locally.
+CONTENTS
+--------
 
-$ cd <this_dir>
-$
-$ # update the Gemfile, modify the line beginning # gem 'beefcake' to refer to
-$ # the patched beefcake dir
-$
-$ bundle install
+Directory structure is the layout for [ruby extensions](http://guides.rubygems.org/gems-with-extensions/)
+
+ * ext: the extension code
+ * lib: the entrypoint grpc ruby library to be used in a 'require' statement
+ * spec: tests
+ * bin: example gRPC clients and servers, e.g,
+```ruby
+# client
+stub = Math::Math::Stub.new('my.test.math.server.com:8080')
+req = Math::DivArgs.new(dividend: 7, divisor: 3)
+logger.info("div(7/3): req=#{req.inspect}")
+resp = stub.div(req, INFINITE_FUTURE)
+logger.info("Answer: #{resp.inspect}")
+```
diff --git a/src/ruby/bin/interop/README.md b/src/ruby/bin/interop/README.md
index 04020868a4c6fdd095b7ce5fd9bb1979280d0be7..84fc66362032d62738504e5055ca7ea8845500d9 100755
--- a/src/ruby/bin/interop/README.md
+++ b/src/ruby/bin/interop/README.md
@@ -1,11 +1,8 @@
 Interop test protos
 ===================
 
-These were generated by a patched version of beefcake and a patched version of
-protoc.
+These ruby classes were generated with protoc v3, using grpc's ruby compiler
+plugin.
 
-- set up and access of the patched versions is described in ../../README.md
-
-The actual test proto is found in Google3 at
-
-- third_party/stubby/testing/proto/test.proto
+- As of 2015/01 protoc v3 is available in the
+[google-protobuf](https://github.com/google/protobuf) repo
diff --git a/src/ruby/bin/interop/interop_server.rb b/src/ruby/bin/interop/interop_server.rb
index 1a08eb97dfef7faffa3df5e104ef6a2664855e6b..83212823f62559cc5454359f9491afd82ba0cd7a 100755
--- a/src/ruby/bin/interop/interop_server.rb
+++ b/src/ruby/bin/interop/interop_server.rb
@@ -145,8 +145,8 @@ class TestTarget < Grpc::Testing::TestService::Service
   end
 
   def half_duplex_call(reqs)
-    # TODO(temiola): clarify the behaviour of the half_duplex_call, it's not
-    # currently used in any tests
+    # TODO: update with unique behaviour of the half_duplex_call if that's
+    # ever required by any of the tests.
     full_duplex_call(reqs)
   end
 end
diff --git a/src/ruby/ext/grpc/extconf.rb b/src/ruby/ext/grpc/extconf.rb
index a6dbbf3aca1ea7b60c574836f6f210e0a1f5ef51..cbf41eda8b7ac613510a425faeedfb2395f5037d 100644
--- a/src/ruby/ext/grpc/extconf.rb
+++ b/src/ruby/ext/grpc/extconf.rb
@@ -68,13 +68,9 @@ $CFLAGS << ' -Wno-return-type '
 $CFLAGS << ' -Wall '
 $CFLAGS << ' -pedantic '
 
-$LDFLAGS << ' -lgrpc -lgpr'
-
-# crash('need grpc lib') unless have_library('grpc', 'grpc_channel_destroy')
-#
-# TODO(temiola): figure out why this stopped working, but the so is built OK
-# and the tests pass
+$LDFLAGS << ' -lgrpc -lgpr -ldl'
 
+crash('need grpc lib') unless have_library('grpc', 'grpc_channel_destroy')
 have_library('grpc', 'grpc_channel_destroy')
 crash('need gpr lib') unless have_library('gpr', 'gpr_now')
 create_makefile('grpc/grpc')
diff --git a/src/ruby/ext/grpc/rb_completion_queue.c b/src/ruby/ext/grpc/rb_completion_queue.c
index c1b74e2606dbf482607780a3a2ba59462873bdc4..47776a991a14bcef0cc111bb5247a9137e83463d 100644
--- a/src/ruby/ext/grpc/rb_completion_queue.c
+++ b/src/ruby/ext/grpc/rb_completion_queue.c
@@ -75,7 +75,7 @@ static void grpc_rb_completion_queue_shutdown_drain(grpc_completion_queue *cq) {
   grpc_completion_queue_shutdown(cq);
   next_call.cq = cq;
   next_call.event = NULL;
-  /* TODO(temiola): the timeout should be a module level constant that defaults
+  /* TODO: the timeout should be a module level constant that defaults
    * to gpr_inf_future.
    *
    * - at the moment this does not work, it stalls.  Using a small timeout like
diff --git a/src/ruby/grpc.gemspec b/src/ruby/grpc.gemspec
index 8d7f44f30e204b3622adac5bb038ae3a6b158813..450362f5a82220a17d2d4f9bea14565f1df32e9b 100755
--- a/src/ruby/grpc.gemspec
+++ b/src/ruby/grpc.gemspec
@@ -5,11 +5,11 @@ require 'grpc/version'
 Gem::Specification.new do |s|
   s.name          = 'grpc'
   s.version       = Google::RPC::VERSION
-  s.authors       = ['One Platform Team']
-  s.email         = 'stubby-team@google.com'
-  s.homepage      = 'http://go/grpc'
+  s.authors       = ['gRPC Authors']
+  s.email         = 'tbetbetbe@gmail.com'
+  s.homepage      = 'https://github.com/google/grpc/tree/master/src/ruby'
   s.summary       = 'Google RPC system in Ruby'
-  s.description   = 'Send RPCs from Ruby'
+  s.description   = 'Send RPCs from Ruby using Google\'s RPC system'
 
   s.files         = `git ls-files`.split("\n")
   s.test_files    = `git ls-files -- spec/*`.split("\n")
diff --git a/src/ruby/lib/grpc/generic/bidi_call.rb b/src/ruby/lib/grpc/generic/bidi_call.rb
index 7653192ad62173c3638bba254c49a993490250a3..099d57151c090b5be60887fd8e1b9cf50a087867 100644
--- a/src/ruby/lib/grpc/generic/bidi_call.rb
+++ b/src/ruby/lib/grpc/generic/bidi_call.rb
@@ -140,7 +140,7 @@ module Google
       # during bidi-streaming, read the requests to send from a separate thread
       # read so that read_loop does not block waiting for requests to read.
       def start_write_loop(requests, is_client: true)
-        Thread.new do  # TODO(temiola) run on a thread pool
+        Thread.new do  # TODO: run on a thread pool
           write_tag = Object.new
           begin
             count = 0
diff --git a/src/ruby/lib/grpc/generic/rpc_server.rb b/src/ruby/lib/grpc/generic/rpc_server.rb
index 5ea3cc94d65a2fd1c1c457508388d1e47562675a..40c5ec118e3c7dbaf5b82c29fdb6b79726aeebfc 100644
--- a/src/ruby/lib/grpc/generic/rpc_server.rb
+++ b/src/ruby/lib/grpc/generic/rpc_server.rb
@@ -233,10 +233,6 @@ module Google
       end
 
       def new_active_server_call(call, new_server_rpc)
-        # TODO(temiola): perhaps reuse the main server completion queue here,
-        # but for now, create a new completion queue per call, pending best
-        # practice usage advice from the c core.
-
         # Accept the call.  This is necessary even if a status is to be sent
         # back immediately
         finished_tag = Object.new
@@ -340,7 +336,7 @@ module Google
           @workers.size.times { schedule { throw :exit } }
           @stopped = true
 
-          # TODO(temiola): allow configuration of the keepalive period
+          # TODO: allow configuration of the keepalive period
           keep_alive = 5
           @stop_mutex.synchronize do
             @stop_cond.wait(@stop_mutex, keep_alive) if @workers.size > 0
diff --git a/src/ruby/lib/grpc/logconfig.rb b/src/ruby/lib/grpc/logconfig.rb
index 6d8e1899a0608e6694c2ead9f4483254b64c27ff..6442f23e895b5b793dad6a1202a7604b727660bf 100644
--- a/src/ruby/lib/grpc/logconfig.rb
+++ b/src/ruby/lib/grpc/logconfig.rb
@@ -34,7 +34,7 @@ include Logging.globally  # logger is accessible everywhere
 Logging.logger.root.appenders = Logging.appenders.stdout
 Logging.logger.root.level = :info
 
-# TODO(temiola): provide command-line configuration for logging
+# TODO: provide command-line configuration for logging
 Logging.logger['Google::RPC'].level = :debug
 Logging.logger['Google::RPC::ActiveCall'].level = :info
 Logging.logger['Google::RPC::BidiCall'].level = :info
diff --git a/src/ruby/spec/client_server_spec.rb b/src/ruby/spec/client_server_spec.rb
index 59b4bbd9d8e4e893e200b8d4b208308d3c33ac03..b2afb0581e5b6eeee0747e2359e30b13ae361dae 100644
--- a/src/ruby/spec/client_server_spec.rb
+++ b/src/ruby/spec/client_server_spec.rb
@@ -290,7 +290,7 @@ shared_examples 'GRPC metadata delivery works OK' do
       expect_next_event_on(@server_queue, WRITE_ACCEPTED, @server_tag)
 
       # there is the HTTP status metadata, though there should not be any
-      # TODO(temiola): update this with the bug number to be resolved
+      # TODO: update this with the bug number to be resolved
       ev = expect_next_event_on(@client_queue, CLIENT_METADATA_READ, @tag)
       expect(ev.result).to eq(':status' => '200')
     end
diff --git a/src/ruby/spec/testdata/README b/src/ruby/spec/testdata/README
index ed72661e974e48da94044ef34d38baf19f3bfe41..cb20dcb49fbafe0f82ccda1544023605e4dfaaac 100755
--- a/src/ruby/spec/testdata/README
+++ b/src/ruby/spec/testdata/README
@@ -1,4 +1 @@
 These are test keys *NOT* to be used in production.
-http://go/keyhunt requires this README
-
-CONFIRMEDTESTKEY
diff --git a/test/core/end2end/cq_verifier.c b/test/core/end2end/cq_verifier.c
index 5c2570540e00da24a27a3e685ec8c2e9b7aafe80..82c0ef1700b47db6551fe908218fcf990f28a4de 100644
--- a/test/core/end2end/cq_verifier.c
+++ b/test/core/end2end/cq_verifier.c
@@ -56,8 +56,8 @@
 typedef struct metadata {
   size_t count;
   size_t cap;
-  const char **keys;
-  const char **values;
+  char **keys;
+  char **values;
 } metadata;
 
 /* details what we expect to find on a single event - and forms a linked
@@ -407,11 +407,11 @@ static metadata *metadata_from_args(va_list args) {
 
     if (md->cap == md->count) {
       md->cap = GPR_MAX(md->cap + 1, md->cap * 3 / 2);
-      md->keys = gpr_realloc(md->keys, sizeof(const char *) * md->cap);
-      md->values = gpr_realloc(md->values, sizeof(const char *) * md->cap);
+      md->keys = gpr_realloc(md->keys, sizeof(char *) * md->cap);
+      md->values = gpr_realloc(md->values, sizeof(char *) * md->cap);
     }
-    md->keys[md->count] = key;
-    md->values[md->count] = value;
+    md->keys[md->count] = (char *)key;
+    md->values[md->count] = (char *)value;
     md->count++;
   }
 }
diff --git a/test/core/transport/transport_end2end_tests.c b/test/core/transport/transport_end2end_tests.c
index 712081bc8aba90617d954b634b306b3262fcc300..5d26ef53b9051d1fe86611c395825e72dc991b24 100644
--- a/test/core/transport/transport_end2end_tests.c
+++ b/test/core/transport/transport_end2end_tests.c
@@ -129,7 +129,8 @@ static void expect_metadata(test_stream *s, int from_client, const char *key,
 /* Convert some number of seconds into a gpr_timespec that many seconds in the
    future */
 static gpr_timespec deadline_from_seconds(double deadline_seconds) {
-  return gpr_time_add(gpr_now(), gpr_time_from_micros(deadline_seconds * 1e6));
+  return gpr_time_add(gpr_now(), 
+                      gpr_time_from_micros((long)(deadline_seconds * 1e6)));
 }
 
 /* Init a test_user_data instance */
@@ -573,7 +574,7 @@ static grpc_transport_setup_result setup_client_transport(
      name   - the name of this test */
 static void begin_test(test_fixture *f, grpc_transport_test_config *config,
                        const char *name) {
-  gpr_timespec timeout = gpr_time_add(gpr_now(), gpr_time_from_micros(100e6));
+  gpr_timespec timeout = gpr_time_add(gpr_now(), gpr_time_from_seconds(100));
 
   gpr_log(GPR_INFO, "BEGIN: %s/%s", name, config->name);
 
diff --git a/test/core/util/test_config.c b/test/core/util/test_config.c
index 6df86b593f4115d11f1a325c5e6dfa5d5e0aea8e..5f3b55da75bc18720065011d4fefc78b3b5b5e38 100644
--- a/test/core/util/test_config.c
+++ b/test/core/util/test_config.c
@@ -48,8 +48,10 @@ static int seed(void) { return _getpid(); }
 #endif
 
 void grpc_test_init(int argc, char **argv) {
+#ifndef GPR_WIN32
   /* disable SIGPIPE */
   signal(SIGPIPE, SIG_IGN);
+#endif
   /* seed rng with pid, so we don't end up with the same random numbers as a
      concurrently running test binary */
   srand(seed());
diff --git a/test/cpp/interop/client.cc b/test/cpp/interop/client.cc
index 04cfeb86cb31835832e26410f4e5cd3a8f41d8b3..2a18ddb72e63a32d9c113de4f2696105f3c8be10 100644
--- a/test/cpp/interop/client.cc
+++ b/test/cpp/interop/client.cc
@@ -188,7 +188,7 @@ void DoResponseStreamingWithSlowConsumer(
   grpc::ClientContext context;
   StreamingOutputCallRequest request;
 
-  for (unsigned int i = 0; i < kNumResponseMessages; ++i) {
+  for (int i = 0; i < kNumResponseMessages; ++i) {
     ResponseParameters* response_parameter = request.add_response_parameters();
     response_parameter->set_size(kResponseMessageSize);
   }
@@ -196,7 +196,7 @@ void DoResponseStreamingWithSlowConsumer(
   std::unique_ptr<grpc::ClientReader<StreamingOutputCallResponse>> stream(
       stub->StreamingOutputCall(&context, &request));
 
-  unsigned int i = 0;
+  int i = 0;
   while (stream->Read(&response)) {
     GPR_ASSERT(response.payload().body() ==
                grpc::string(kResponseMessageSize, '\0'));
diff --git a/tools/dockerfile/grpc_go/Dockerfile b/tools/dockerfile/grpc_go/Dockerfile
new file mode 100644
index 0000000000000000000000000000000000000000..ab463b2a00670221baced99b3c7f076767b1a15d
--- /dev/null
+++ b/tools/dockerfile/grpc_go/Dockerfile
@@ -0,0 +1,27 @@
+# Dockerfile for gRPC Go
+FROM golang:1.4
+
+# Install SSH to that Go source can be pulled securely.
+RUN apt-get update && apt-get install -y ssh
+
+# Install a GitHub SSH service credential that gives access to the GitHub repo while it's private
+#
+# TODO: remove this once the repo is public
+ADD .ssh .ssh
+RUN chmod 600 /.ssh/github.rsa
+RUN mkdir -p $HOME/.ssh && echo 'Host github.com' > $HOME/.ssh/config
+RUN echo "    IdentityFile /.ssh/github.rsa" >> $HOME/.ssh/config
+RUN echo 'StrictHostKeyChecking no' >> $HOME/.ssh/config
+
+# Force go get to use the GitHub ssh url instead of https, and use the SSH creds
+RUN git config --global url."git@github.com:".insteadOf "https://github.com/"
+
+# Get the source from GitHub
+RUN go get github.com/google/grpc-go
+
+# Build the interop client and server
+RUN cd src/github.com/google/grpc-go/interop/client && go install
+RUN cd src/github.com/google/grpc-go/interop/server && go install
+
+# Specify the default command such that the interop server runs on its known testing port
+CMD ["/bin/bash", "-c 'cd src/github.com/google/grpc-go/interop/server && go run server.go --use_tls=true --port=8020'"]
diff --git a/tools/dockerfile/grpc_go/README.md b/tools/dockerfile/grpc_go/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..0d6ad3e391c42f55a5a194dc49a55f70209f94ff
--- /dev/null
+++ b/tools/dockerfile/grpc_go/README.md
@@ -0,0 +1,4 @@
+GRPC Go Dockerfile
+==================
+
+Dockerfile for gRPC Go development, testing and deployment.
diff --git a/tools/gce_setup/grpc_docker.sh b/tools/gce_setup/grpc_docker.sh
index bf776126b5af45584e69ac7d0e1fd7d9650d5e67..bfa4c808be4f02aa33e345562313bbc3b1ff4b69 100755
--- a/tools/gce_setup/grpc_docker.sh
+++ b/tools/gce_setup/grpc_docker.sh
@@ -86,6 +86,7 @@ grpc_add_docker_user() {
 }
 
 _grpc_update_image_args() {
+  echo "image_args $@"
   # default the host, root storage uri and docker file root
   grpc_gs_root='gs://tmp-grpc-dev/admin/'
   grpc_dockerfile_root='tools/dockerfile'
@@ -95,7 +96,7 @@ _grpc_update_image_args() {
   # see if -p or -z is used to override the the project or zone
   local OPTIND
   local OPTARG
-  while getopts :r:d:h name
+  while getopts :r:d:h: name
   do
     case $name in
       d)  grpc_dockerfile_root=$OPTARG ;;
@@ -261,7 +262,7 @@ _grpc_set_project_and_zone() {
   local OPTIND
   local OPTARG
   local arg_func
-  while getopts :p:z:f:n name
+  while getopts :np:z:f: name
   do
     case $name in
       f)   declare -F $OPTARG >> /dev/null && {
@@ -392,6 +393,65 @@ grpc_interop_test_args() {
   }
 }
 
+_grpc_sync_scripts_args() {
+  grpc_gce_script_root='tools/gce_setup'
+
+  local OPTIND
+  local OPTARG
+  while getopts :s: name
+  do
+    case $name in
+      s)  grpc_gce_script_root=$OPTARG ;;
+      :)  continue ;; # ignore -s without args, just use the defaults
+      \?)  echo "-$OPTARG: unknown flag; it's ignored" 1>&2;  continue ;;
+    esac
+  done
+  shift $((OPTIND-1))
+
+  [[ -d $grpc_gce_script_root ]] || {
+    echo "Could not locate gce script dir: $grpc_gce_script_root" 1>&2
+    return 1
+  }
+
+  [[ $# -lt 1  ]] && {
+    echo "$FUNCNAME: missing arg: host1 [host2 ... hostN]" 1>&2
+    return 1
+  }
+  grpc_hosts="$@"
+}
+
+# Updates the latest version of the support scripts on some hosts.
+#
+# call-seq;
+#   grpc_sync_scripts <server_name1>, <server_name2> .. <server_name3>
+#
+# Updates the GCE docker instance <server_name>
+grpc_sync_scripts() {
+  _grpc_ensure_gcloud_ssh || return 1;
+
+  # declare vars local so that they don't pollute the shell environment
+  # where they this func is used.
+  local grpc_zone grpc_project dry_run  # set by _grpc_set_project_and_zone
+  local grpc_hosts grpc_gce_script_root
+
+  # set the project zone and check that all necessary args are provided
+  _grpc_set_project_and_zone -f _grpc_sync_scripts_args "$@" || return 1
+
+  local func_lib="shared_startup_funcs.sh"
+  local gce_func_lib="/var/local/startup_scripts/$func_lib"
+  local project_opt="--project $grpc_project"
+  local zone_opt="--zone $grpc_zone"
+  local host
+  for host in $grpc_hosts
+  do
+    gce_has_instance $grpc_project $host || return 1;
+    # Update the remote copy of the GCE func library.
+    local src_func_lib="$grpc_gce_script_root/$func_lib"
+    local rmt_func_lib="$host:$gce_func_lib"
+    gcloud compute copy-files $src_func_lib $rmt_func_lib $project_opt $zone_opt || return 1
+  done
+}
+
 grpc_sync_images_args() {
   [[ $# -lt 1  ]] && {
     echo "$FUNCNAME: missing arg: host1 [host2 ... hostN]" 1>&2
@@ -412,7 +472,6 @@ grpc_sync_images() {
   # declare vars local so that they don't pollute the shell environment
   # where they this func is used.
   local grpc_zone grpc_project dry_run  # set by _grpc_set_project_and_zone
-  # set by grpc_sync_images
   local grpc_hosts
 
   # set the project zone and check that all necessary args are provided
@@ -425,7 +484,7 @@ grpc_sync_images() {
   local host
   for host in $grpc_hosts
   do
-    gce_has_instance $grpc_project $h || return 1;
+    gce_has_instance $grpc_project $host || return 1;
     local ssh_cmd="bash -l -c \"$cmd\""
     echo "will run:"
     echo "  $ssh_cmd"
@@ -575,6 +634,18 @@ grpc_interop_gen_ruby_cmd() {
   echo $the_cmd
 }
 
+# constructs the full dockerized Go interop test cmd.
+#
+# call-seq:
+#   flags= .... # generic flags to include the command
+#   cmd=$($grpc_gen_test_cmd $flags)
+grpc_interop_gen_go_cmd() {
+  local cmd_prefix="sudo docker run grpc/go bin/bash -c";
+  local test_script="cd /go/src/github.com/google/grpc-go/interop/client";
+  local test_script+=" && go run client.go --use_tls=true";
+  local the_cmd="$cmd_prefix '$test_script $@ 1>&2'";
+}
+
 # constructs the full dockerized java interop test cmd.
 #
 # call-seq:
@@ -605,4 +676,4 @@ grpc_interop_gen_php_cmd() {
 }
 
 
-# TODO(grpc-team): add grpc_interop_gen_xxx_cmd for python|cxx|nodejs|go
+# TODO(grpc-team): add grpc_interop_gen_xxx_cmd for python|cxx|nodejs
diff --git a/tools/gce_setup/shared_startup_funcs.sh b/tools/gce_setup/shared_startup_funcs.sh
index 9c747466a911118908b637ad2aa8e17c571fdd79..f1dbca9a2e65137ababdbd72b2773c2bb013f2ec 100755
--- a/tools/gce_setup/shared_startup_funcs.sh
+++ b/tools/gce_setup/shared_startup_funcs.sh
@@ -367,11 +367,12 @@ grpc_docker_launch_registry() {
 grpc_docker_pull_known() {
   local addr=$1
   [[ -n $addr ]] || addr="0.0.0.0:5000"
-  local known="base cxx php_base php ruby_base ruby java_base java"
+  local known="base cxx php_base php ruby_base ruby java_base java go"
   echo "... pulling docker images for '$known'"
   for i in $known
   do
-    sudo docker pull ${addr}/grpc/$i \
+    echo "<--- grpc/$i"
+    sudo docker pull ${addr}/grpc/$i > /dev/null 2>&1 \
       && sudo docker tag ${addr}/grpc/$i grpc/$i || {
       # log and continue
       echo "docker op error:  could not pull ${addr}/grpc/$i"
@@ -402,10 +403,15 @@ grpc_dockerfile_install() {
 
   [[ -d $dockerfile_dir ]] || { echo "$FUNCNAME: not a valid dir: $dockerfile_dir"; return 1; }
 
-  # For grpc/base, sync the ssh key into the  .ssh dir in the dockerfile context
-
+  # For specific base images, sync the ssh key into the .ssh dir in the dockerfile context
   [[ $image_label == "grpc/base" ]] && {
-    grpc_docker_sync_github_key $dockerfile_dir/.ssh || return 1;
+    grpc_docker_sync_github_key $dockerfile_dir/.ssh 'base_ssh_key'|| return 1;
+  }
+  [[ $image_label == "grpc/go" ]] && {
+    grpc_docker_sync_github_key $dockerfile_dir/.ssh 'go_ssh_key'|| return 1;
+  }
+  [[ $image_label == "grpc/java_base" ]] && {
+    grpc_docker_sync_github_key $dockerfile_dir/.ssh 'java_base_ssh_key'|| return 1;
   }
 
   # TODO(temiola): maybe make cache/no-cache a func option?
@@ -445,6 +451,9 @@ grpc_docker_sync_github_key() {
   local target_dir=$1
   [[ -n $target_dir ]] || { echo "$FUNCNAME: missing arg: target_dir" >&2; return 1; }
 
+  local key_file=$2
+  [[ -n $key_file ]] || { echo "$FUNCNAME: missing arg: key_file" >&2; return 1; }
+
   # determine the admin root; the parent of the dockerfile root,
   local gs_dockerfile_root=$(load_metadata "attributes/gs_dockerfile_root")
   [[ -n $gs_dockerfile_root ]] || {
@@ -454,7 +463,7 @@ grpc_docker_sync_github_key() {
   local gcs_admin_root=$(dirname $gs_dockerfile_root)
 
   # cp the file from gsutil to a known local area
-  local gcs_key_path=$gcs_admin_root/github/ssh_key
+  local gcs_key_path=$gcs_admin_root/github/$key_file
   local local_key_path=$target_dir/github.rsa
   mkdir -p $target_dir || {
     echo "$FUNCNAME: could not create dir: $target_dir" 1>&2
diff --git a/tools/run_tests/build_python.sh b/tools/run_tests/build_python.sh
new file mode 100755
index 0000000000000000000000000000000000000000..6899ac7fe3e5b9731d5278a713323769456620ac
--- /dev/null
+++ b/tools/run_tests/build_python.sh
@@ -0,0 +1,10 @@
+#!/bin/bash
+
+set -ex
+
+# change to grpc repo root
+cd $(dirname $0)/../..
+
+root=`pwd`
+virtualenv python2.7_virtual_environment
+python2.7_virtual_environment/bin/pip install enum34==1.0.4 futures==2.2.0
diff --git a/tools/run_tests/run_python.sh b/tools/run_tests/run_python.sh
new file mode 100755
index 0000000000000000000000000000000000000000..0d5ed0238dbaeef7e512d83f96cf34afbb750d56
--- /dev/null
+++ b/tools/run_tests/run_python.sh
@@ -0,0 +1,10 @@
+#!/bin/bash
+
+set -ex
+
+# change to grpc repo root
+cd $(dirname $0)/../..
+
+root=`pwd`
+python2.7_virtual_environment/bin/python2.7 -B -m unittest discover -s src/python -p '*.py'
+python3.4 -B -m unittest discover -s src/python -p '*.py'
diff --git a/tools/run_tests/run_tests.py b/tools/run_tests/run_tests.py
index 15c523731bdcb6129710e30b4c5fd06ec967a386..da849f04cb0f5c0dfaaa81c58ad975bfed3ff1fb 100755
--- a/tools/run_tests/run_tests.py
+++ b/tools/run_tests/run_tests.py
@@ -75,6 +75,21 @@ class PhpLanguage(object):
     return [['tools/run_tests/build_php.sh']]
 
 
+class PythonLanguage(object):
+
+  def __init__(self):
+    self.allow_hashing = False
+
+  def test_binaries(self, config):
+    return ['tools/run_tests/run_python.sh']
+
+  def make_targets(self):
+    return[]
+
+  def build_steps(self):
+    return [['tools/run_tests/build_python.sh']]
+
+
 # different configurations we can run under
 _CONFIGS = {
     'dbg': SimpleConfig('dbg'),
@@ -92,7 +107,8 @@ _DEFAULT = ['dbg', 'opt']
 _LANGUAGES = {
     'c++': CLanguage('cxx', 'c++'),
     'c': CLanguage('c', 'c'),
-    'php': PhpLanguage()
+    'php': PhpLanguage(),
+    'python': PythonLanguage(),
 }
 
 # parse command line
diff --git a/tools/run_tests/tests.json b/tools/run_tests/tests.json
index ec838649e0087618b38a218526b036ee541ead8a..90571eaec6c229fe8062e44ca9738b9889c9b2ff 100644
--- a/tools/run_tests/tests.json
+++ b/tools/run_tests/tests.json
@@ -261,6 +261,10 @@
     "language": "c++", 
     "name": "end2end_test"
   }, 
+  {
+    "language": "c++", 
+    "name": "tips_client_test"
+  }, 
   {
     "language": "c++", 
     "name": "status_test"
diff --git a/vsprojects/vs2013/grpc.vcxproj b/vsprojects/vs2013/grpc.vcxproj
index 05a9966c0e00a9752dd800a1a20b15efb1e687a3..8249272f78ceccd8353a4378b9d0f27af4ff2300 100644
--- a/vsprojects/vs2013/grpc.vcxproj
+++ b/vsprojects/vs2013/grpc.vcxproj
@@ -121,7 +121,9 @@
     <ClInclude Include="..\..\src\core\iomgr\pollset.h" />
     <ClInclude Include="..\..\src\core\iomgr\pollset_kick.h" />
     <ClInclude Include="..\..\src\core\iomgr\pollset_kick_posix.h" />
+    <ClInclude Include="..\..\src\core\iomgr\pollset_kick_windows.h" />
     <ClInclude Include="..\..\src\core\iomgr\pollset_posix.h" />
+    <ClInclude Include="..\..\src\core\iomgr\pollset_windows.h" />
     <ClInclude Include="..\..\src\core\iomgr\resolve_address.h" />
     <ClInclude Include="..\..\src\core\iomgr\sockaddr.h" />
     <ClInclude Include="..\..\src\core\iomgr\sockaddr_posix.h" />
@@ -254,6 +256,8 @@
     </ClCompile>
     <ClCompile Include="..\..\src\core\iomgr\pollset_posix.c">
     </ClCompile>
+    <ClCompile Include="..\..\src\core\iomgr\pollset_windows.c">
+    </ClCompile>
     <ClCompile Include="..\..\src\core\iomgr\resolve_address_posix.c">
     </ClCompile>
     <ClCompile Include="..\..\src\core\iomgr\sockaddr_utils.c">
diff --git a/vsprojects/vs2013/grpc_unsecure.vcxproj b/vsprojects/vs2013/grpc_unsecure.vcxproj
index 05a9966c0e00a9752dd800a1a20b15efb1e687a3..8249272f78ceccd8353a4378b9d0f27af4ff2300 100644
--- a/vsprojects/vs2013/grpc_unsecure.vcxproj
+++ b/vsprojects/vs2013/grpc_unsecure.vcxproj
@@ -121,7 +121,9 @@
     <ClInclude Include="..\..\src\core\iomgr\pollset.h" />
     <ClInclude Include="..\..\src\core\iomgr\pollset_kick.h" />
     <ClInclude Include="..\..\src\core\iomgr\pollset_kick_posix.h" />
+    <ClInclude Include="..\..\src\core\iomgr\pollset_kick_windows.h" />
     <ClInclude Include="..\..\src\core\iomgr\pollset_posix.h" />
+    <ClInclude Include="..\..\src\core\iomgr\pollset_windows.h" />
     <ClInclude Include="..\..\src\core\iomgr\resolve_address.h" />
     <ClInclude Include="..\..\src\core\iomgr\sockaddr.h" />
     <ClInclude Include="..\..\src\core\iomgr\sockaddr_posix.h" />
@@ -254,6 +256,8 @@
     </ClCompile>
     <ClCompile Include="..\..\src\core\iomgr\pollset_posix.c">
     </ClCompile>
+    <ClCompile Include="..\..\src\core\iomgr\pollset_windows.c">
+    </ClCompile>
     <ClCompile Include="..\..\src\core\iomgr\resolve_address_posix.c">
     </ClCompile>
     <ClCompile Include="..\..\src\core\iomgr\sockaddr_utils.c">