diff --git a/Makefile b/Makefile
index 29bcd70e1ff218c721bd943f6161f1e0cb48f504..0da2a0416501df51bb06cd16170d45c8b2ee58be 100644
--- a/Makefile
+++ b/Makefile
@@ -310,7 +310,7 @@ E = @echo
 Q = @
 endif
 
-VERSION = 0.9.1.0
+VERSION = 0.10.0.0
 
 CPPFLAGS_NO_ARCH += $(addprefix -I, $(INCLUDES)) $(addprefix -D, $(DEFINES))
 CPPFLAGS += $(CPPFLAGS_NO_ARCH) $(ARCH_FLAGS)
diff --git a/build.json b/build.json
index bf7bde7f49d053aabf7924204eff0ca6ad55430d..3f5ff607a43287c5624686c6dcc3c4f8a29fa864 100644
--- a/build.json
+++ b/build.json
@@ -6,8 +6,8 @@
     "#": "The public version number of the library.",
     "version": {
       "major": 0,
-      "minor": 9,
-      "micro": 1,
+      "minor": 10,
+      "micro": 0,
       "build": 0
     }
   },
diff --git a/gRPC.podspec b/gRPC.podspec
index ad621cd2e8a4b127f147eb94d55fb46b4834414b..19f69d308af5d4016f9b0d9a830bbec387acc23e 100644
--- a/gRPC.podspec
+++ b/gRPC.podspec
@@ -1,3 +1,5 @@
+
+
 Pod::Spec.new do |s|
   s.name     = 'gRPC'
   s.version  = '0.6.0'
@@ -23,8 +25,8 @@ Pod::Spec.new do |s|
 
   # Core cross-platform gRPC library, written in C.
   s.subspec 'C-Core' do |cs|
-    cs.source_files = 'src/core/**/*.{h,c}', 'include/grpc/*.h', 'include/grpc/**/*.h'
-    cs.private_header_files = 'src/core/**/*.h'
+    cs.source_files = 'src/core/support/env.h', 'src/core/support/file.h', 'src/core/support/murmur_hash.h', 'src/core/support/grpc_string.h', 'src/core/support/string_win32.h', 'src/core/support/thd_internal.h', 'include/grpc/support/alloc.h', 'include/grpc/support/atm.h', 'include/grpc/support/atm_gcc_atomic.h', 'include/grpc/support/atm_gcc_sync.h', 'include/grpc/support/atm_win32.h', 'include/grpc/support/cancellable_platform.h', 'include/grpc/support/cmdline.h', 'include/grpc/support/cpu.h', 'include/grpc/support/histogram.h', 'include/grpc/support/host_port.h', 'include/grpc/support/log.h', 'include/grpc/support/log_win32.h', 'include/grpc/support/port_platform.h', 'include/grpc/support/slice.h', 'include/grpc/support/slice_buffer.h', 'include/grpc/support/string_util.h', 'include/grpc/support/subprocess.h', 'include/grpc/support/sync.h', 'include/grpc/support/sync_generic.h', 'include/grpc/support/sync_posix.h', 'include/grpc/support/sync_win32.h', 'include/grpc/support/thd.h', 'include/grpc/support/grpc_time.h', 'include/grpc/support/tls.h', 'include/grpc/support/tls_gcc.h', 'include/grpc/support/tls_msvc.h', 'include/grpc/support/tls_pthread.h', 'include/grpc/support/useful.h', 'src/core/support/alloc.c', 'src/core/support/cancellable.c', 'src/core/support/cmdline.c', 'src/core/support/cpu_iphone.c', 'src/core/support/cpu_linux.c', 'src/core/support/cpu_posix.c', 'src/core/support/cpu_windows.c', 'src/core/support/env_linux.c', 'src/core/support/env_posix.c', 'src/core/support/env_win32.c', 'src/core/support/file.c', 'src/core/support/file_posix.c', 'src/core/support/file_win32.c', 'src/core/support/histogram.c', 'src/core/support/host_port.c', 'src/core/support/log.c', 'src/core/support/log_android.c', 'src/core/support/log_linux.c', 'src/core/support/log_posix.c', 'src/core/support/log_win32.c', 'src/core/support/murmur_hash.c', 'src/core/support/slice.c', 'src/core/support/slice_buffer.c', 'src/core/support/string.c', 'src/core/support/string_posix.c', 'src/core/support/string_win32.c', 'src/core/support/subprocess_posix.c', 'src/core/support/sync.c', 'src/core/support/sync_posix.c', 'src/core/support/sync_win32.c', 'src/core/support/thd.c', 'src/core/support/thd_posix.c', 'src/core/support/thd_win32.c', 'src/core/support/time.c', 'src/core/support/time_posix.c', 'src/core/support/time_win32.c', 'src/core/support/tls_pthread.c', 'src/core/httpcli/format_request.h', 'src/core/httpcli/httpcli.h', 'src/core/httpcli/httpcli_security_connector.h', 'src/core/httpcli/parser.h', 'src/core/security/auth_filters.h', 'src/core/security/base64.h', 'src/core/security/credentials.h', 'src/core/security/json_token.h', 'src/core/security/secure_endpoint.h', 'src/core/security/secure_transport_setup.h', 'src/core/security/security_connector.h', 'src/core/security/security_context.h', 'src/core/tsi/fake_transport_security.h', 'src/core/tsi/ssl_transport_security.h', 'src/core/tsi/transport_security.h', 'src/core/tsi/transport_security_interface.h', 'src/core/census/grpc_context.h', 'src/core/channel/channel_args.h', 'src/core/channel/channel_stack.h', 'src/core/channel/child_channel.h', 'src/core/channel/client_channel.h', 'src/core/channel/client_setup.h', 'src/core/channel/connected_channel.h', 'src/core/channel/http_client_filter.h', 'src/core/channel/http_server_filter.h', 'src/core/channel/noop_filter.h', 'src/core/compression/message_compress.h', 'src/core/debug/trace.h', 'src/core/iomgr/alarm.h', 'src/core/iomgr/alarm_heap.h', 'src/core/iomgr/alarm_internal.h', 'src/core/iomgr/endpoint.h', 'src/core/iomgr/endpoint_pair.h', 'src/core/iomgr/fd_posix.h', 'src/core/iomgr/iocp_windows.h', 'src/core/iomgr/iomgr.h', 'src/core/iomgr/iomgr_internal.h', 'src/core/iomgr/iomgr_posix.h', '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', 'src/core/iomgr/sockaddr_utils.h', 'src/core/iomgr/sockaddr_win32.h', 'src/core/iomgr/socket_utils_posix.h', 'src/core/iomgr/socket_windows.h', 'src/core/iomgr/tcp_client.h', 'src/core/iomgr/tcp_posix.h', 'src/core/iomgr/tcp_server.h', 'src/core/iomgr/tcp_windows.h', 'src/core/iomgr/time_averaged_stats.h', 'src/core/iomgr/wakeup_fd_pipe.h', 'src/core/iomgr/wakeup_fd_posix.h', 'src/core/json/json.h', 'src/core/json/json_common.h', 'src/core/json/json_reader.h', 'src/core/json/json_writer.h', 'src/core/profiling/timers.h', 'src/core/profiling/timers_preciseclock.h', 'src/core/surface/byte_buffer_queue.h', 'src/core/surface/call.h', 'src/core/surface/channel.h', 'src/core/surface/client.h', 'src/core/surface/completion_queue.h', 'src/core/surface/event_string.h', 'src/core/surface/init.h', 'src/core/surface/server.h', 'src/core/surface/surface_trace.h', 'src/core/transport/chttp2/alpn.h', 'src/core/transport/chttp2/bin_encoder.h', 'src/core/transport/chttp2/frame.h', 'src/core/transport/chttp2/frame_data.h', 'src/core/transport/chttp2/frame_goaway.h', 'src/core/transport/chttp2/frame_ping.h', 'src/core/transport/chttp2/frame_rst_stream.h', 'src/core/transport/chttp2/frame_settings.h', 'src/core/transport/chttp2/frame_window_update.h', 'src/core/transport/chttp2/hpack_parser.h', 'src/core/transport/chttp2/hpack_table.h', 'src/core/transport/chttp2/http2_errors.h', 'src/core/transport/chttp2/huffsyms.h', 'src/core/transport/chttp2/status_conversion.h', 'src/core/transport/chttp2/stream_encoder.h', 'src/core/transport/chttp2/stream_map.h', 'src/core/transport/chttp2/timeout_encoding.h', 'src/core/transport/chttp2/varint.h', 'src/core/transport/chttp2_transport.h', 'src/core/transport/metadata.h', 'src/core/transport/stream_op.h', 'src/core/transport/transport.h', 'src/core/transport/transport_impl.h', 'src/core/census/context.h', 'include/grpc/grpc_security.h', 'include/grpc/byte_buffer.h', 'include/grpc/byte_buffer_reader.h', 'include/grpc/compression.h', 'include/grpc/grpc.h', 'include/grpc/status.h', 'include/grpc/census.h', 'src/core/httpcli/format_request.c', 'src/core/httpcli/httpcli.c', 'src/core/httpcli/httpcli_security_connector.c', 'src/core/httpcli/parser.c', 'src/core/security/base64.c', 'src/core/security/client_auth_filter.c', 'src/core/security/credentials.c', 'src/core/security/credentials_metadata.c', 'src/core/security/credentials_posix.c', 'src/core/security/credentials_win32.c', 'src/core/security/google_default_credentials.c', 'src/core/security/json_token.c', 'src/core/security/secure_endpoint.c', 'src/core/security/secure_transport_setup.c', 'src/core/security/security_connector.c', 'src/core/security/security_context.c', 'src/core/security/server_auth_filter.c', 'src/core/security/server_secure_chttp2.c', 'src/core/surface/init_secure.c', 'src/core/surface/secure_channel_create.c', 'src/core/tsi/fake_transport_security.c', 'src/core/tsi/ssl_transport_security.c', 'src/core/tsi/transport_security.c', 'src/core/census/grpc_context.c', 'src/core/channel/channel_args.c', 'src/core/channel/channel_stack.c', 'src/core/channel/child_channel.c', 'src/core/channel/client_channel.c', 'src/core/channel/client_setup.c', 'src/core/channel/connected_channel.c', 'src/core/channel/http_client_filter.c', 'src/core/channel/http_server_filter.c', 'src/core/channel/noop_filter.c', 'src/core/compression/algorithm.c', 'src/core/compression/message_compress.c', 'src/core/debug/trace.c', 'src/core/iomgr/alarm.c', 'src/core/iomgr/alarm_heap.c', 'src/core/iomgr/endpoint.c', 'src/core/iomgr/endpoint_pair_posix.c', 'src/core/iomgr/endpoint_pair_windows.c', 'src/core/iomgr/fd_posix.c', 'src/core/iomgr/iocp_windows.c', 'src/core/iomgr/iomgr.c', 'src/core/iomgr/iomgr_posix.c', 'src/core/iomgr/iomgr_windows.c', 'src/core/iomgr/pollset_kick.c', 'src/core/iomgr/pollset_multipoller_with_epoll.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/resolve_address_windows.c', 'src/core/iomgr/sockaddr_utils.c', 'src/core/iomgr/socket_utils_common_posix.c', 'src/core/iomgr/socket_utils_linux.c', 'src/core/iomgr/socket_utils_posix.c', 'src/core/iomgr/socket_windows.c', 'src/core/iomgr/tcp_client_posix.c', 'src/core/iomgr/tcp_client_windows.c', 'src/core/iomgr/tcp_posix.c', 'src/core/iomgr/tcp_server_posix.c', 'src/core/iomgr/tcp_server_windows.c', 'src/core/iomgr/tcp_windows.c', 'src/core/iomgr/time_averaged_stats.c', 'src/core/iomgr/wakeup_fd_eventfd.c', 'src/core/iomgr/wakeup_fd_nospecial.c', 'src/core/iomgr/wakeup_fd_pipe.c', 'src/core/iomgr/wakeup_fd_posix.c', 'src/core/json/json.c', 'src/core/json/json_reader.c', 'src/core/json/json_string.c', 'src/core/json/json_writer.c', 'src/core/profiling/basic_timers.c', 'src/core/profiling/stap_timers.c', 'src/core/surface/byte_buffer.c', 'src/core/surface/byte_buffer_queue.c', 'src/core/surface/byte_buffer_reader.c', 'src/core/surface/call.c', 'src/core/surface/call_details.c', 'src/core/surface/call_log_batch.c', 'src/core/surface/channel.c', 'src/core/surface/channel_create.c', 'src/core/surface/client.c', 'src/core/surface/completion_queue.c', 'src/core/surface/event_string.c', 'src/core/surface/init.c', 'src/core/surface/lame_client.c', 'src/core/surface/metadata_array.c', 'src/core/surface/server.c', 'src/core/surface/server_chttp2.c', 'src/core/surface/server_create.c', 'src/core/surface/surface_trace.c', 'src/core/transport/chttp2/alpn.c', 'src/core/transport/chttp2/bin_encoder.c', 'src/core/transport/chttp2/frame_data.c', 'src/core/transport/chttp2/frame_goaway.c', 'src/core/transport/chttp2/frame_ping.c', 'src/core/transport/chttp2/frame_rst_stream.c', 'src/core/transport/chttp2/frame_settings.c', 'src/core/transport/chttp2/frame_window_update.c', 'src/core/transport/chttp2/hpack_parser.c', 'src/core/transport/chttp2/hpack_table.c', 'src/core/transport/chttp2/huffsyms.c', 'src/core/transport/chttp2/status_conversion.c', 'src/core/transport/chttp2/stream_encoder.c', 'src/core/transport/chttp2/stream_map.c', 'src/core/transport/chttp2/timeout_encoding.c', 'src/core/transport/chttp2/varint.c', 'src/core/transport/chttp2_transport.c', 'src/core/transport/metadata.c', 'src/core/transport/stream_op.c', 'src/core/transport/transport.c', 'src/core/transport/transport_op_string.c', 'src/core/census/context.c', 'src/core/census/initialize.c', 
+    cs.private_header_files = 'src/core/support/env.h', 'src/core/support/file.h', 'src/core/support/murmur_hash.h', 'src/core/support/string.h', 'src/core/support/string_win32.h', 'src/core/support/thd_internal.h', 'src/core/httpcli/format_request.h', 'src/core/httpcli/httpcli.h', 'src/core/httpcli/httpcli_security_connector.h', 'src/core/httpcli/parser.h', 'src/core/security/auth_filters.h', 'src/core/security/base64.h', 'src/core/security/credentials.h', 'src/core/security/json_token.h', 'src/core/security/secure_endpoint.h', 'src/core/security/secure_transport_setup.h', 'src/core/security/security_connector.h', 'src/core/security/security_context.h', 'src/core/tsi/fake_transport_security.h', 'src/core/tsi/ssl_transport_security.h', 'src/core/tsi/transport_security.h', 'src/core/tsi/transport_security_interface.h', 'src/core/census/grpc_context.h', 'src/core/channel/channel_args.h', 'src/core/channel/channel_stack.h', 'src/core/channel/child_channel.h', 'src/core/channel/client_channel.h', 'src/core/channel/client_setup.h', 'src/core/channel/connected_channel.h', 'src/core/channel/http_client_filter.h', 'src/core/channel/http_server_filter.h', 'src/core/channel/noop_filter.h', 'src/core/compression/message_compress.h', 'src/core/debug/trace.h', 'src/core/iomgr/alarm.h', 'src/core/iomgr/alarm_heap.h', 'src/core/iomgr/alarm_internal.h', 'src/core/iomgr/endpoint.h', 'src/core/iomgr/endpoint_pair.h', 'src/core/iomgr/fd_posix.h', 'src/core/iomgr/iocp_windows.h', 'src/core/iomgr/iomgr.h', 'src/core/iomgr/iomgr_internal.h', 'src/core/iomgr/iomgr_posix.h', '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', 'src/core/iomgr/sockaddr_utils.h', 'src/core/iomgr/sockaddr_win32.h', 'src/core/iomgr/socket_utils_posix.h', 'src/core/iomgr/socket_windows.h', 'src/core/iomgr/tcp_client.h', 'src/core/iomgr/tcp_posix.h', 'src/core/iomgr/tcp_server.h', 'src/core/iomgr/tcp_windows.h', 'src/core/iomgr/time_averaged_stats.h', 'src/core/iomgr/wakeup_fd_pipe.h', 'src/core/iomgr/wakeup_fd_posix.h', 'src/core/json/json.h', 'src/core/json/json_common.h', 'src/core/json/json_reader.h', 'src/core/json/json_writer.h', 'src/core/profiling/timers.h', 'src/core/profiling/timers_preciseclock.h', 'src/core/surface/byte_buffer_queue.h', 'src/core/surface/call.h', 'src/core/surface/channel.h', 'src/core/surface/client.h', 'src/core/surface/completion_queue.h', 'src/core/surface/event_string.h', 'src/core/surface/init.h', 'src/core/surface/server.h', 'src/core/surface/surface_trace.h', 'src/core/transport/chttp2/alpn.h', 'src/core/transport/chttp2/bin_encoder.h', 'src/core/transport/chttp2/frame.h', 'src/core/transport/chttp2/frame_data.h', 'src/core/transport/chttp2/frame_goaway.h', 'src/core/transport/chttp2/frame_ping.h', 'src/core/transport/chttp2/frame_rst_stream.h', 'src/core/transport/chttp2/frame_settings.h', 'src/core/transport/chttp2/frame_window_update.h', 'src/core/transport/chttp2/hpack_parser.h', 'src/core/transport/chttp2/hpack_table.h', 'src/core/transport/chttp2/http2_errors.h', 'src/core/transport/chttp2/huffsyms.h', 'src/core/transport/chttp2/status_conversion.h', 'src/core/transport/chttp2/stream_encoder.h', 'src/core/transport/chttp2/stream_map.h', 'src/core/transport/chttp2/timeout_encoding.h', 'src/core/transport/chttp2/varint.h', 'src/core/transport/chttp2_transport.h', 'src/core/transport/metadata.h', 'src/core/transport/stream_op.h', 'src/core/transport/transport.h', 'src/core/transport/transport_impl.h', 'src/core/census/context.h', 
     cs.header_mappings_dir = '.'
     # The core library includes its headers as either "src/core/..." or "grpc/...", meaning we have
     # to tell XCode to look for headers under the "include" subdirectory too.
diff --git a/include/grpc++/async_unary_call.h b/include/grpc++/async_unary_call.h
index 786f8c718469015b9c6bf0e5ad5735a2262ec5f0..abb6308782db4263e38a1772642797b2c010e05a 100644
--- a/include/grpc++/async_unary_call.h
+++ b/include/grpc++/async_unary_call.h
@@ -117,7 +117,7 @@ class ServerAsyncResponseWriter GRPC_FINAL
       ctx_->sent_initial_metadata_ = true;
     }
     // The response is dropped if the status is not OK.
-    if (status.IsOk()) {
+    if (status.ok()) {
       finish_buf_.AddSendMessage(msg);
     }
     finish_buf_.AddServerSendStatus(&ctx_->trailing_metadata_, status);
@@ -125,7 +125,7 @@ class ServerAsyncResponseWriter GRPC_FINAL
   }
 
   void FinishWithError(const Status& status, void* tag) {
-    GPR_ASSERT(!status.IsOk());
+    GPR_ASSERT(!status.ok());
     finish_buf_.Reset(tag);
     if (!ctx_->sent_initial_metadata_) {
       finish_buf_.AddSendInitialMetadata(&ctx_->initial_metadata_);
diff --git a/include/grpc++/client_context.h b/include/grpc++/client_context.h
index 6d9015f278cc852ab4a0d37943e3ff87b6384844..ecf4cc7f7b5ffb05d175795c63d60dc9129ca6ad 100644
--- a/include/grpc++/client_context.h
+++ b/include/grpc++/client_context.h
@@ -41,6 +41,7 @@
 #include <grpc/support/log.h>
 #include <grpc/support/time.h>
 #include <grpc++/config.h>
+#include <grpc++/status.h>
 #include <grpc++/time.h>
 
 struct grpc_call;
@@ -53,7 +54,6 @@ class ChannelInterface;
 class CompletionQueue;
 class Credentials;
 class RpcMethod;
-class Status;
 template <class R>
 class ClientReader;
 template <class W>
diff --git a/include/grpc++/impl/client_unary_call.h b/include/grpc++/impl/client_unary_call.h
index 0e8aeed78162bca78d20f506a4cca815cad320e8..2f234fd3ac7b11eabbcd8e050714ae119aef70d7 100644
--- a/include/grpc++/impl/client_unary_call.h
+++ b/include/grpc++/impl/client_unary_call.h
@@ -35,6 +35,7 @@
 #define GRPCXX_IMPL_CLIENT_UNARY_CALL_H
 
 #include <grpc++/config.h>
+#include <grpc++/status.h>
 
 namespace grpc {
 
@@ -42,7 +43,6 @@ class ChannelInterface;
 class ClientContext;
 class CompletionQueue;
 class RpcMethod;
-class Status;
 
 // Wrapper that performs a blocking unary call
 Status BlockingUnaryCall(ChannelInterface* channel, const RpcMethod& method,
diff --git a/include/grpc++/impl/service_type.h b/include/grpc++/impl/service_type.h
index bc39bb82ac32683db8956cc9f283a592b2909fcd..25e437edad7f0ce17bedea928c649cccf6ff01c8 100644
--- a/include/grpc++/impl/service_type.h
+++ b/include/grpc++/impl/service_type.h
@@ -35,6 +35,7 @@
 #define GRPCXX_IMPL_SERVICE_TYPE_H
 
 #include <grpc++/config.h>
+#include <grpc++/status.h>
 
 namespace grpc {
 
@@ -44,7 +45,6 @@ class RpcService;
 class Server;
 class ServerCompletionQueue;
 class ServerContext;
-class Status;
 
 class SynchronousService {
  public:
diff --git a/include/grpc++/server.h b/include/grpc++/server.h
index 50a24163219bee7cc5f6de163da49eca48b031a4..2cfeb359fc08c2162e20f064ea65c61ffcdc56dd 100644
--- a/include/grpc++/server.h
+++ b/include/grpc++/server.h
@@ -77,6 +77,7 @@ class Server GRPC_FINAL : public GrpcLibrary,
 
   class SyncRequest;
   class AsyncRequest;
+  class ShutdownRequest;
 
   // ServerBuilder use only
   Server(ThreadPoolInterface* thread_pool, bool thread_pool_owned,
diff --git a/include/grpc++/status.h b/include/grpc++/status.h
index 8073319eab89c3b2a9b305621eb9563525eb5d6d..fb8526ddce0ec29d66215c357a0080302de44f68 100644
--- a/include/grpc++/status.h
+++ b/include/grpc++/status.h
@@ -42,18 +42,17 @@ namespace grpc {
 class Status {
  public:
   Status() : code_(StatusCode::OK) {}
-  explicit Status(StatusCode code) : code_(code) {}
   Status(StatusCode code, const grpc::string& details)
       : code_(code), details_(details) {}
 
   // Pre-defined special status objects.
   static const Status& OK;
-  static const Status& Cancelled;
+  static const Status& CANCELLED;
 
-  StatusCode code() const { return code_; }
-  grpc::string details() const { return details_; }
+  StatusCode error_code() const { return code_; }
+  grpc::string error_message() const { return details_; }
 
-  bool IsOk() const { return code_ == StatusCode::OK; }
+  bool ok() const { return code_ == StatusCode::OK; }
 
  private:
   StatusCode code_;
diff --git a/include/grpc++/stream.h b/include/grpc++/stream.h
index c836f98c2a48f53cab9965ab75a17c8ba8b6c774..472911e62b295f21cff53e08532bde2c528d2a2a 100644
--- a/include/grpc++/stream.h
+++ b/include/grpc++/stream.h
@@ -615,7 +615,7 @@ class ServerAsyncReader GRPC_FINAL : public ServerAsyncStreamingInterface,
       ctx_->sent_initial_metadata_ = true;
     }
     // The response is dropped if the status is not OK.
-    if (status.IsOk()) {
+    if (status.ok()) {
       finish_buf_.AddSendMessage(msg);
     }
     finish_buf_.AddServerSendStatus(&ctx_->trailing_metadata_, status);
@@ -623,7 +623,7 @@ class ServerAsyncReader GRPC_FINAL : public ServerAsyncStreamingInterface,
   }
 
   void FinishWithError(const Status& status, void* tag) {
-    GPR_ASSERT(!status.IsOk());
+    GPR_ASSERT(!status.ok());
     finish_buf_.Reset(tag);
     if (!ctx_->sent_initial_metadata_) {
       finish_buf_.AddSendInitialMetadata(&ctx_->initial_metadata_);
diff --git a/include/grpc/byte_buffer.h b/include/grpc/byte_buffer.h
index 6d08474d8c158bf9a367ebe3d9f1e48bbcd2791b..1b51569f24a9120792f46d162a9915a3c968ef94 100644
--- a/include/grpc/byte_buffer.h
+++ b/include/grpc/byte_buffer.h
@@ -103,6 +103,10 @@ void grpc_byte_buffer_reader_destroy(grpc_byte_buffer_reader *reader);
 int grpc_byte_buffer_reader_next(grpc_byte_buffer_reader *reader,
                                  gpr_slice *slice);
 
+/** Returns a RAW byte buffer instance from the output of \a reader. */
+grpc_byte_buffer *grpc_raw_byte_buffer_from_reader(
+    grpc_byte_buffer_reader *reader);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/include/grpc/grpc.h b/include/grpc/grpc.h
index 40f14dce302c82632f895521f7e15f7f0ad3ca8d..722ce4f365248fdba49e36822e77e8c1434f7cf0 100644
--- a/include/grpc/grpc.h
+++ b/include/grpc/grpc.h
@@ -99,7 +99,8 @@ typedef struct {
     These configuration options are modelled as key-value pairs as defined
     by grpc_arg; keys are strings to allow easy backwards-compatible extension
     by arbitrary parties.
-    All evaluation is performed at channel creation time. */
+    All evaluation is performed at channel creation time (i.e. the values in
+    this structure need only live through the creation invocation). */
 typedef struct {
   size_t num_args;
   grpc_arg *args;
@@ -271,6 +272,8 @@ typedef struct grpc_op {
        After the operation completes, call grpc_metadata_array_destroy on this
        value, or reuse it in a future op. */
     grpc_metadata_array *recv_initial_metadata;
+    /* ownership of the byte buffer is moved to the caller; the caller must call
+       grpc_byte_buffer_destroy on this value, or reuse it in a future op. */
     grpc_byte_buffer **recv_message;
     struct {
       /* ownership of the array is with the caller, but ownership of the
@@ -316,7 +319,7 @@ typedef struct grpc_op {
 } grpc_op;
 
 /** Initialize the grpc library.
-    
+
     It is not safe to call any other grpc functions before calling this.
     (To avoid overhead, little checking is done, and some things may work. We
     do not warrant that they will continue to do so in future revisions of this
@@ -324,7 +327,7 @@ typedef struct grpc_op {
 void grpc_init(void);
 
 /** Shut down the grpc library.
-    
+
     No memory is used by grpc after this call returns, nor are any instructions
     executing within the grpc library.
     Prior to calling, all application owned grpc objects must have been
@@ -371,7 +374,8 @@ void grpc_completion_queue_destroy(grpc_completion_queue *cq);
 
 /* Create a call given a grpc_channel, in order to call 'method'. The request
    is not sent until grpc_call_invoke is called. All completions are sent to
-   'completion_queue'. */
+   'completion_queue'. 'method' and 'host' need only live through the invocation
+   of this function. */
 grpc_call *grpc_channel_create_call(grpc_channel *channel,
                                     grpc_completion_queue *completion_queue,
                                     const char *method, const char *host,
@@ -396,8 +400,9 @@ grpc_call_error grpc_call_start_batch(grpc_call *call, const grpc_op *ops,
 
 /* Create a client channel to 'target'. Additional channel level configuration
    MAY be provided by grpc_channel_args, though the expectation is that most
-   clients will want to simply pass NULL. See grpc_channel_args definition
-   for more on this. */
+   clients will want to simply pass NULL. See grpc_channel_args definition for
+   more on this. The data in 'args' need only live through the invocation of
+   this function. */
 grpc_channel *grpc_channel_create(const char *target,
                                   const grpc_channel_args *args);
 
@@ -468,7 +473,8 @@ grpc_call_error grpc_server_request_registered_call(
 
 /* Create a server. Additional configuration for each incoming channel can
    be specified with args. If no additional configuration is needed, args can
-   be NULL. See grpc_channel_args for more. */
+   be NULL. See grpc_channel_args for more. The data in 'args' need only live
+   through the invocation of this function. */
 grpc_server *grpc_server_create(const grpc_channel_args *args);
 
 /* Register a completion queue with the server. Must be done for any completion
@@ -488,18 +494,20 @@ void grpc_server_start(grpc_server *server);
 /* Begin shutting down a server.
    After completion, no new calls or connections will be admitted.
    Existing calls will be allowed to complete.
-   Shutdown is idempotent. */
-void grpc_server_shutdown(grpc_server *server);
-
-/* As per grpc_server_shutdown, but send a GRPC_OP_COMPLETE event when
-   there are no more calls being serviced.
+   Send a GRPC_OP_COMPLETE event when there are no more calls being serviced.
    Shutdown is idempotent, and all tags will be notified at once if multiple
    grpc_server_shutdown_and_notify calls are made. */
-void grpc_server_shutdown_and_notify(grpc_server *server, void *tag);
+void grpc_server_shutdown_and_notify(grpc_server *server,
+                                     grpc_completion_queue *cq, void *tag);
+
+/* Cancel all in-progress calls.
+   Only usable after shutdown. */
+void grpc_server_cancel_all_calls(grpc_server *server);
 
 /* Destroy a server.
-   Forcefully cancels all existing calls.
-   Implies grpc_server_shutdown() if one was not previously performed. */
+   Shutdown must have completed beforehand (i.e. all tags generated by
+   grpc_server_shutdown_and_notify must have been received, and at least
+   one call to grpc_server_shutdown_and_notify must have been made). */
 void grpc_server_destroy(grpc_server *server);
 
 /** Enable or disable a tracer.
diff --git a/src/compiler/cpp_generator.cc b/src/compiler/cpp_generator.cc
index c00c85bb9009eada52b0389338aa80023fac1811..6cd615019b09ba8e32a0cc4af7882a73cd6939a7 100644
--- a/src/compiler/cpp_generator.cc
+++ b/src/compiler/cpp_generator.cc
@@ -854,7 +854,7 @@ void PrintSourceServerMethod(grpc::protobuf::io::Printer *printer,
     printer->Print("  (void) response;\n");
     printer->Print(
         "  return ::grpc::Status("
-        "::grpc::StatusCode::UNIMPLEMENTED);\n");
+        "::grpc::StatusCode::UNIMPLEMENTED, \"\");\n");
     printer->Print("}\n\n");
   } else if (ClientOnlyStreaming(method)) {
     printer->Print(*vars,
@@ -867,7 +867,7 @@ void PrintSourceServerMethod(grpc::protobuf::io::Printer *printer,
     printer->Print("  (void) response;\n");
     printer->Print(
         "  return ::grpc::Status("
-        "::grpc::StatusCode::UNIMPLEMENTED);\n");
+        "::grpc::StatusCode::UNIMPLEMENTED, \"\");\n");
     printer->Print("}\n\n");
   } else if (ServerOnlyStreaming(method)) {
     printer->Print(*vars,
@@ -880,7 +880,7 @@ void PrintSourceServerMethod(grpc::protobuf::io::Printer *printer,
     printer->Print("  (void) writer;\n");
     printer->Print(
         "  return ::grpc::Status("
-        "::grpc::StatusCode::UNIMPLEMENTED);\n");
+        "::grpc::StatusCode::UNIMPLEMENTED, \"\");\n");
     printer->Print("}\n\n");
   } else if (BidiStreaming(method)) {
     printer->Print(*vars,
@@ -892,7 +892,7 @@ void PrintSourceServerMethod(grpc::protobuf::io::Printer *printer,
     printer->Print("  (void) stream;\n");
     printer->Print(
         "  return ::grpc::Status("
-        "::grpc::StatusCode::UNIMPLEMENTED);\n");
+        "::grpc::StatusCode::UNIMPLEMENTED, \"\");\n");
     printer->Print("}\n\n");
   }
 }
diff --git a/src/core/iomgr/pollset_posix.c b/src/core/iomgr/pollset_posix.c
index a8e60690022a428b0096fb5f70beb7af29b75324..d2f615271e465b061237aae9878f72ae3b0aee64 100644
--- a/src/core/iomgr/pollset_posix.c
+++ b/src/core/iomgr/pollset_posix.c
@@ -174,8 +174,6 @@ void grpc_pollset_del_fd(grpc_pollset *pollset, grpc_fd *fd) {
 int grpc_pollset_work(grpc_pollset *pollset, gpr_timespec deadline) {
   /* pollset->mu already held */
   gpr_timespec now = gpr_now();
-  /* FIXME(ctiller): see below */
-  gpr_timespec maximum_deadline = gpr_time_add(now, gpr_time_from_seconds(1));
   int r;
   if (gpr_time_cmp(now, deadline) > 0) {
     return 0;
@@ -186,14 +184,25 @@ int grpc_pollset_work(grpc_pollset *pollset, gpr_timespec deadline) {
   if (grpc_alarm_check(&pollset->mu, now, &deadline)) {
     return 1;
   }
-  /* FIXME(ctiller): we should not clamp deadline, however we have some
-     stuck at shutdown bugs that this resolves */
-  if (gpr_time_cmp(deadline, maximum_deadline) > 0) {
-    deadline = maximum_deadline;
+  if (pollset->shutting_down) {
+    return 1;
   }
   gpr_tls_set(&g_current_thread_poller, (gpr_intptr)pollset);
   r = pollset->vtable->maybe_work(pollset, deadline, now, 1);
   gpr_tls_set(&g_current_thread_poller, 0);
+  if (pollset->shutting_down) {
+    if (pollset->counter > 0) {
+      grpc_pollset_kick(pollset);
+    } else if (pollset->in_flight_cbs == 0) {
+      gpr_mu_unlock(&pollset->mu);
+      pollset->shutdown_done_cb(pollset->shutdown_done_arg);
+      /* Continuing to access pollset here is safe -- it is the caller's
+       * responsibility to not destroy when it has outstanding calls to
+       * grpc_pollset_work.
+       * TODO(dklempner): Can we refactor the shutdown logic to avoid this? */
+      gpr_mu_lock(&pollset->mu);
+    }
+  }
   return r;
 }
 
@@ -201,13 +210,19 @@ void grpc_pollset_shutdown(grpc_pollset *pollset,
                            void (*shutdown_done)(void *arg),
                            void *shutdown_done_arg) {
   int in_flight_cbs;
+  int counter;
   gpr_mu_lock(&pollset->mu);
   pollset->shutting_down = 1;
   in_flight_cbs = pollset->in_flight_cbs;
+  counter = pollset->counter;
   pollset->shutdown_done_cb = shutdown_done;
   pollset->shutdown_done_arg = shutdown_done_arg;
+  if (counter > 0) {
+    grpc_pollset_kick(pollset);
+  }
   gpr_mu_unlock(&pollset->mu);
-  if (in_flight_cbs == 0) {
+
+  if (in_flight_cbs == 0 && counter == 0) {
     shutdown_done(shutdown_done_arg);
   }
 }
@@ -294,7 +309,7 @@ static void unary_poll_do_promote(void *args, int success) {
   pollset->in_flight_cbs--;
   if (pollset->shutting_down) {
     /* We don't care about this pollset anymore. */
-    if (pollset->in_flight_cbs == 0) {
+    if (pollset->in_flight_cbs == 0 && pollset->counter == 0) {
       do_shutdown_cb = 1;
     }
   } else if (grpc_fd_is_orphaned(fd)) {
diff --git a/src/core/support/sync.c b/src/core/support/sync.c
index ccfe1e25f45a585c11baaf225b974d3d2455b04f..856b5adb862bd5d3fb745338d437b566733bcd9e 100644
--- a/src/core/support/sync.c
+++ b/src/core/support/sync.c
@@ -118,7 +118,9 @@ void gpr_refn(gpr_refcount *r, int n) {
 }
 
 int gpr_unref(gpr_refcount *r) {
-  return gpr_atm_full_fetch_add(&r->count, -1) == 1;
+  gpr_atm prior = gpr_atm_full_fetch_add(&r->count, -1);
+  GPR_ASSERT(prior > 0);
+  return prior == 1;
 }
 
 void gpr_stats_init(gpr_stats_counter *c, gpr_intptr n) {
diff --git a/src/core/surface/byte_buffer.c b/src/core/surface/byte_buffer.c
index 4817e004549ce02e6250e77c29277232c1b28c97..a930949f2d4690fea58ee987205659cc66c7e1f7 100644
--- a/src/core/surface/byte_buffer.c
+++ b/src/core/surface/byte_buffer.c
@@ -55,6 +55,20 @@ grpc_byte_buffer *grpc_raw_compressed_byte_buffer_create(
   return bb;
 }
 
+grpc_byte_buffer *grpc_raw_byte_buffer_from_reader(
+    grpc_byte_buffer_reader *reader) {
+  grpc_byte_buffer *bb = malloc(sizeof(grpc_byte_buffer));
+  gpr_slice slice;
+  bb->type = GRPC_BB_RAW;
+  bb->data.raw.compression = GRPC_COMPRESS_NONE;
+  gpr_slice_buffer_init(&bb->data.raw.slice_buffer);
+
+  while (grpc_byte_buffer_reader_next(reader, &slice)) {
+    gpr_slice_buffer_add(&bb->data.raw.slice_buffer, slice);
+  }
+  return bb;
+}
+
 grpc_byte_buffer *grpc_byte_buffer_copy(grpc_byte_buffer *bb) {
   switch (bb->type) {
     case GRPC_BB_RAW:
diff --git a/src/core/surface/completion_queue.c b/src/core/surface/completion_queue.c
index 8c9ca48a059776510b4f62372d111104a02dee2d..b48fbace315b477000ab2a07911f5f42e986e449 100644
--- a/src/core/surface/completion_queue.c
+++ b/src/core/surface/completion_queue.c
@@ -83,7 +83,8 @@ grpc_completion_queue *grpc_completion_queue_create(void) {
   memset(cc, 0, sizeof(*cc));
   /* Initial ref is dropped by grpc_completion_queue_shutdown */
   gpr_ref_init(&cc->refs, 1);
-  gpr_ref_init(&cc->owning_refs, 1);
+  /* One for destroy(), one for pollset_shutdown */
+  gpr_ref_init(&cc->owning_refs, 2);
   grpc_pollset_init(&cc->pollset);
   cc->allow_polling = 1;
   return cc;
@@ -95,14 +96,14 @@ void grpc_cq_internal_ref(grpc_completion_queue *cc) {
 
 static void on_pollset_destroy_done(void *arg) {
   grpc_completion_queue *cc = arg;
-  grpc_pollset_destroy(&cc->pollset);
-  gpr_free(cc);
+  grpc_cq_internal_unref(cc);
 }
 
 void grpc_cq_internal_unref(grpc_completion_queue *cc) {
   if (gpr_unref(&cc->owning_refs)) {
     GPR_ASSERT(cc->queue == NULL);
-    grpc_pollset_shutdown(&cc->pollset, on_pollset_destroy_done, cc);
+    grpc_pollset_destroy(&cc->pollset);
+    gpr_free(cc);
   }
 }
 
@@ -145,25 +146,25 @@ void grpc_cq_begin_op(grpc_completion_queue *cc, grpc_call *call) {
 
 /* Signal the end of an operation - if this is the last waiting-to-be-queued
    event, then enter shutdown mode */
-static void end_op_locked(grpc_completion_queue *cc,
-                          grpc_completion_type type) {
-  if (gpr_unref(&cc->refs)) {
-    GPR_ASSERT(!cc->shutdown);
-    GPR_ASSERT(cc->shutdown_called);
-    cc->shutdown = 1;
-    gpr_cv_broadcast(GRPC_POLLSET_CV(&cc->pollset));
-  }
-}
-
 void grpc_cq_end_op(grpc_completion_queue *cc, void *tag, grpc_call *call,
                     int success) {
   event *ev;
+  int shutdown = 0;
   gpr_mu_lock(GRPC_POLLSET_MU(&cc->pollset));
   ev = add_locked(cc, GRPC_OP_COMPLETE, tag, call);
   ev->base.success = success;
-  end_op_locked(cc, GRPC_OP_COMPLETE);
+  if (gpr_unref(&cc->refs)) {
+    GPR_ASSERT(!cc->shutdown);
+    GPR_ASSERT(cc->shutdown_called);
+    cc->shutdown = 1;
+    gpr_cv_broadcast(GRPC_POLLSET_CV(&cc->pollset));
+    shutdown = 1;
+  }
   gpr_mu_unlock(GRPC_POLLSET_MU(&cc->pollset));
   if (call) GRPC_CALL_INTERNAL_UNREF(call, "cq", 0);
+  if (shutdown) {
+    grpc_pollset_shutdown(&cc->pollset, on_pollset_destroy_done, cc);
+  }
 }
 
 /* Create a GRPC_QUEUE_SHUTDOWN event without queuing it anywhere */
@@ -179,6 +180,7 @@ grpc_event grpc_completion_queue_next(grpc_completion_queue *cc,
   event *ev = NULL;
   grpc_event ret;
 
+  grpc_cq_internal_ref(cc);
   gpr_mu_lock(GRPC_POLLSET_MU(&cc->pollset));
   for (;;) {
     if (cc->queue != NULL) {
@@ -214,6 +216,7 @@ grpc_event grpc_completion_queue_next(grpc_completion_queue *cc,
       memset(&ret, 0, sizeof(ret));
       ret.type = GRPC_QUEUE_TIMEOUT;
       GRPC_SURFACE_TRACE_RETURNED_EVENT(cc, &ret);
+      grpc_cq_internal_unref(cc);
       return ret;
     }
   }
@@ -221,6 +224,7 @@ grpc_event grpc_completion_queue_next(grpc_completion_queue *cc,
   ret = ev->base;
   gpr_free(ev);
   GRPC_SURFACE_TRACE_RETURNED_EVENT(cc, &ret);
+  grpc_cq_internal_unref(cc);
   return ret;
 }
 
@@ -258,6 +262,7 @@ grpc_event grpc_completion_queue_pluck(grpc_completion_queue *cc, void *tag,
   event *ev = NULL;
   grpc_event ret;
 
+  grpc_cq_internal_ref(cc);
   gpr_mu_lock(GRPC_POLLSET_MU(&cc->pollset));
   for (;;) {
     if ((ev = pluck_event(cc, tag))) {
@@ -276,6 +281,7 @@ grpc_event grpc_completion_queue_pluck(grpc_completion_queue *cc, void *tag,
       memset(&ret, 0, sizeof(ret));
       ret.type = GRPC_QUEUE_TIMEOUT;
       GRPC_SURFACE_TRACE_RETURNED_EVENT(cc, &ret);
+      grpc_cq_internal_unref(cc);
       return ret;
     }
   }
@@ -283,6 +289,7 @@ grpc_event grpc_completion_queue_pluck(grpc_completion_queue *cc, void *tag,
   ret = ev->base;
   gpr_free(ev);
   GRPC_SURFACE_TRACE_RETURNED_EVENT(cc, &ret);
+  grpc_cq_internal_unref(cc);
   return ret;
 }
 
@@ -299,6 +306,7 @@ void grpc_completion_queue_shutdown(grpc_completion_queue *cc) {
     cc->shutdown = 1;
     gpr_cv_broadcast(GRPC_POLLSET_CV(&cc->pollset));
     gpr_mu_unlock(GRPC_POLLSET_MU(&cc->pollset));
+    grpc_pollset_shutdown(&cc->pollset, on_pollset_destroy_done, cc);
   }
 }
 
diff --git a/src/core/surface/server.c b/src/core/surface/server.c
index 73995202a4d5673935bc78895f42d79472af620e..825ef66804bf417a4351b1fa39d575997f5c1785 100644
--- a/src/core/surface/server.c
+++ b/src/core/surface/server.c
@@ -127,6 +127,11 @@ struct channel_data {
   grpc_iomgr_closure finish_destroy_channel_closure;
 };
 
+typedef struct shutdown_tag {
+  void *tag;
+  grpc_completion_queue *cq;
+} shutdown_tag;
+
 struct grpc_server {
   size_t channel_filter_count;
   const grpc_channel_filter **channel_filters;
@@ -137,14 +142,14 @@ struct grpc_server {
   size_t cq_count;
 
   gpr_mu mu;
-  gpr_cv cv;
 
   registered_method *registered_methods;
   requested_call_array requested_calls;
 
   gpr_uint8 shutdown;
+  gpr_uint8 shutdown_published;
   size_t num_shutdown_tags;
-  void **shutdown_tags;
+  shutdown_tag *shutdown_tags;
 
   call_data *lists[CALL_LIST_COUNT];
   channel_data root_channel_data;
@@ -261,29 +266,32 @@ static void server_ref(grpc_server *server) {
   gpr_ref(&server->internal_refcount);
 }
 
-static void server_unref(grpc_server *server) {
+static void server_delete(grpc_server *server) {
   registered_method *rm;
   size_t i;
+  grpc_channel_args_destroy(server->channel_args);
+  gpr_mu_destroy(&server->mu);
+  gpr_free(server->channel_filters);
+  requested_call_array_destroy(&server->requested_calls);
+  while ((rm = server->registered_methods) != NULL) {
+    server->registered_methods = rm->next;
+    gpr_free(rm->method);
+    gpr_free(rm->host);
+    requested_call_array_destroy(&rm->requested);
+    gpr_free(rm);
+  }
+  for (i = 0; i < server->cq_count; i++) {
+    grpc_cq_internal_unref(server->cqs[i]);
+  }
+  gpr_free(server->cqs);
+  gpr_free(server->pollsets);
+  gpr_free(server->shutdown_tags);
+  gpr_free(server);
+}
+
+static void server_unref(grpc_server *server) {
   if (gpr_unref(&server->internal_refcount)) {
-    grpc_channel_args_destroy(server->channel_args);
-    gpr_mu_destroy(&server->mu);
-    gpr_cv_destroy(&server->cv);
-    gpr_free(server->channel_filters);
-    requested_call_array_destroy(&server->requested_calls);
-    while ((rm = server->registered_methods) != NULL) {
-      server->registered_methods = rm->next;
-      gpr_free(rm->method);
-      gpr_free(rm->host);
-      requested_call_array_destroy(&rm->requested);
-      gpr_free(rm);
-    }
-    for (i = 0; i < server->cq_count; i++) {
-      grpc_cq_internal_unref(server->cqs[i]);
-    }
-    gpr_free(server->cqs);
-    gpr_free(server->pollsets);
-    gpr_free(server->shutdown_tags);
-    gpr_free(server);
+    server_delete(server);
   }
 }
 
@@ -378,6 +386,26 @@ static void kill_zombie(void *elem, int success) {
   grpc_call_destroy(grpc_call_from_top_element(elem));
 }
 
+static int num_listeners(grpc_server *server) {
+  listener *l;
+  int n = 0;
+  for (l = server->listeners; l; l = l->next) {
+    n++;
+  }
+  return n;
+}
+
+static void maybe_finish_shutdown(grpc_server *server) {
+  size_t i;
+  if (server->shutdown && !server->shutdown_published && server->lists[ALL_CALLS] == NULL && server->listeners_destroyed == num_listeners(server)) {
+    server->shutdown_published = 1;
+    for (i = 0; i < server->num_shutdown_tags; i++) {
+      grpc_cq_end_op(server->shutdown_tags[i].cq, server->shutdown_tags[i].tag,
+                     NULL, 1);
+    }
+  }
+}
+
 static grpc_mdelem *server_filter(void *user_data, grpc_mdelem *md) {
   grpc_call_element *elem = user_data;
   channel_data *chand = elem->channel_data;
@@ -441,6 +469,9 @@ static void server_on_recv(void *ptr, int success) {
         grpc_iomgr_add_callback(&calld->kill_zombie_closure);
 
       }
+      if (call_list_remove(calld, ALL_CALLS)) {
+        maybe_finish_shutdown(chand->server);
+      }
       gpr_mu_unlock(&chand->server->mu);
       break;
   }
@@ -539,19 +570,15 @@ static void init_call_elem(grpc_call_element *elem,
 static void destroy_call_elem(grpc_call_element *elem) {
   channel_data *chand = elem->channel_data;
   call_data *calld = elem->call_data;
-  size_t i, j;
+  int removed[CALL_LIST_COUNT];
+  size_t i;
 
   gpr_mu_lock(&chand->server->mu);
   for (i = 0; i < CALL_LIST_COUNT; i++) {
-    call_list_remove(elem->call_data, i);
+    removed[i] = call_list_remove(elem->call_data, i);
   }
-  if (chand->server->shutdown && chand->server->lists[ALL_CALLS] == NULL) {
-    for (i = 0; i < chand->server->num_shutdown_tags; i++) {
-      for (j = 0; j < chand->server->cq_count; j++) {
-        grpc_cq_end_op(chand->server->cqs[j], chand->server->shutdown_tags[i],
-                       NULL, 1);
-      }
-    }
+  if (removed[ALL_CALLS]) {
+    maybe_finish_shutdown(chand->server);
   }
   gpr_mu_unlock(&chand->server->mu);
 
@@ -646,7 +673,6 @@ grpc_server *grpc_server_create_from_filters(grpc_channel_filter **filters,
   memset(server, 0, sizeof(grpc_server));
 
   gpr_mu_init(&server->mu);
-  gpr_cv_init(&server->cv);
 
   /* decremented by grpc_server_destroy */
   gpr_ref_init(&server->internal_refcount, 1);
@@ -806,38 +832,28 @@ grpc_transport_setup_result grpc_server_setup_transport(
   return result;
 }
 
-static int num_listeners(grpc_server *server) {
-  listener *l;
-  int n = 0;
-  for (l = server->listeners; l; l = l->next) {
-    n++;
-  }
-  return n;
-}
-
-static void shutdown_internal(grpc_server *server, gpr_uint8 have_shutdown_tag,
-                              void *shutdown_tag) {
+void grpc_server_shutdown_and_notify(grpc_server *server,
+                                     grpc_completion_queue *cq, void *tag) {
   listener *l;
   requested_call_array requested_calls;
   channel_data **channels;
   channel_data *c;
   size_t nchannels;
-  size_t i, j;
+  size_t i;
   grpc_channel_op op;
   grpc_channel_element *elem;
   registered_method *rm;
+  shutdown_tag *sdt;
 
   /* lock, and gather up some stuff to do */
   gpr_mu_lock(&server->mu);
-  if (have_shutdown_tag) {
-    for (i = 0; i < server->cq_count; i++) {
-      grpc_cq_begin_op(server->cqs[i], NULL);
-    }
-    server->shutdown_tags =
-        gpr_realloc(server->shutdown_tags,
-                    sizeof(void *) * (server->num_shutdown_tags + 1));
-    server->shutdown_tags[server->num_shutdown_tags++] = shutdown_tag;
-  }
+  grpc_cq_begin_op(cq, NULL);
+  server->shutdown_tags =
+      gpr_realloc(server->shutdown_tags,
+                  sizeof(shutdown_tag) * (server->num_shutdown_tags + 1));
+  sdt = &server->shutdown_tags[server->num_shutdown_tags++];
+  sdt->tag = tag;
+  sdt->cq = cq;
   if (server->shutdown) {
     gpr_mu_unlock(&server->mu);
     return;
@@ -878,13 +894,7 @@ static void shutdown_internal(grpc_server *server, gpr_uint8 have_shutdown_tag,
   }
 
   server->shutdown = 1;
-  if (server->lists[ALL_CALLS] == NULL) {
-    for (i = 0; i < server->num_shutdown_tags; i++) {
-      for (j = 0; j < server->cq_count; j++) {
-        grpc_cq_end_op(server->cqs[j], server->shutdown_tags[i], NULL, 1);
-      }
-    }
-  }
+  maybe_finish_shutdown(server);
   gpr_mu_unlock(&server->mu);
 
   for (i = 0; i < nchannels; i++) {
@@ -914,46 +924,64 @@ static void shutdown_internal(grpc_server *server, gpr_uint8 have_shutdown_tag,
   }
 }
 
-void grpc_server_shutdown(grpc_server *server) {
-  shutdown_internal(server, 0, NULL);
-}
-
-void grpc_server_shutdown_and_notify(grpc_server *server, void *tag) {
-  shutdown_internal(server, 1, tag);
-}
-
 void grpc_server_listener_destroy_done(void *s) {
   grpc_server *server = s;
   gpr_mu_lock(&server->mu);
   server->listeners_destroyed++;
-  gpr_cv_signal(&server->cv);
+  maybe_finish_shutdown(server);
   gpr_mu_unlock(&server->mu);
 }
 
-void grpc_server_destroy(grpc_server *server) {
-  channel_data *c;
-  listener *l;
-  size_t i;
+void grpc_server_cancel_all_calls(grpc_server *server) {
   call_data *calld;
+  grpc_call **calls;
+  size_t call_count;
+  size_t call_capacity;
+  int is_first = 1;
+  size_t i;
 
   gpr_mu_lock(&server->mu);
-  if (!server->shutdown) {
+
+  GPR_ASSERT(server->shutdown);
+
+  if (!server->lists[ALL_CALLS]) {
     gpr_mu_unlock(&server->mu);
-    grpc_server_shutdown(server);
-    gpr_mu_lock(&server->mu);
+    return;
   }
 
-  while (server->listeners_destroyed != num_listeners(server)) {
-    for (i = 0; i < server->cq_count; i++) {
-      gpr_mu_unlock(&server->mu);
-      grpc_cq_hack_spin_pollset(server->cqs[i]);
-      gpr_mu_lock(&server->mu);
+  call_capacity = 8;
+  call_count = 0;
+  calls = gpr_malloc(sizeof(grpc_call *) * call_capacity);
+
+  for (calld = server->lists[ALL_CALLS]; calld != server->lists[ALL_CALLS] || is_first; calld = calld->links[ALL_CALLS].next) {
+    if (call_count == call_capacity) {
+      call_capacity *= 2;
+      calls = gpr_realloc(calls, sizeof(grpc_call *) * call_capacity);
     }
+    calls[call_count++] = calld->call;
+    GRPC_CALL_INTERNAL_REF(calld->call, "cancel_all");
+    is_first = 0;
+  }
+
+  gpr_mu_unlock(&server->mu);
 
-    gpr_cv_wait(&server->cv, &server->mu,
-                gpr_time_add(gpr_now(), gpr_time_from_millis(100)));
+  for (i = 0; i < call_count; i++) {
+    grpc_call_cancel_with_status(calls[i], GRPC_STATUS_UNAVAILABLE, "Unavailable");
+    GRPC_CALL_INTERNAL_UNREF(calls[i], "cancel_all", 1);
   }
 
+  gpr_free(calls);
+}
+
+void grpc_server_destroy(grpc_server *server) {
+  channel_data *c;
+  listener *l;
+  call_data *calld;
+
+  gpr_mu_lock(&server->mu);
+  GPR_ASSERT(server->shutdown || !server->listeners);
+  GPR_ASSERT(server->listeners_destroyed == num_listeners(server));
+
   while (server->listeners) {
     l = server->listeners;
     server->listeners = l->next;
@@ -962,10 +990,6 @@ void grpc_server_destroy(grpc_server *server) {
 
   while ((calld = call_list_remove_head(&server->lists[PENDING_START],
                                         PENDING_START)) != NULL) {
-    /* TODO(dgq): If we knew the size of the call list (or an upper bound), we
-     * could allocate all the memory for the closures in advance in a single
-     * chunk */
-    gpr_log(GPR_DEBUG, "server destroys call %p", calld->call);
     calld->state = ZOMBIED;
     grpc_iomgr_closure_init(
         &calld->kill_zombie_closure, kill_zombie,
diff --git a/src/cpp/client/client_unary_call.cc b/src/cpp/client/client_unary_call.cc
index 7e7ea78bcde380689a770d87522f59c133512e16..55e589306f117ccfb624223159680d44eb830983 100644
--- a/src/cpp/client/client_unary_call.cc
+++ b/src/cpp/client/client_unary_call.cc
@@ -57,7 +57,7 @@ Status BlockingUnaryCall(ChannelInterface* channel, const RpcMethod& method,
   buf.AddClientSendClose();
   buf.AddClientRecvStatus(context, &status);
   call.PerformOps(&buf);
-  GPR_ASSERT((cq.Pluck(&buf) && buf.got_message) || !status.IsOk());
+  GPR_ASSERT((cq.Pluck(&buf) && buf.got_message) || !status.ok());
   return status;
 }
 
diff --git a/src/cpp/common/call.cc b/src/cpp/common/call.cc
index 3212f770f42be33ce1b92d1634723f925b3e2f3a..edce6396bd4cb267372f46de6044ff803f6ba881 100644
--- a/src/cpp/common/call.cc
+++ b/src/cpp/common/call.cc
@@ -214,8 +214,8 @@ void CallOpBuffer::AddServerSendStatus(
     trailing_metadata_count_ = 0;
   }
   send_status_available_ = true;
-  send_status_code_ = static_cast<grpc_status_code>(status.code());
-  send_status_details_ = status.details();
+  send_status_code_ = static_cast<grpc_status_code>(status.error_code());
+  send_status_details_ = status.error_message();
 }
 
 void CallOpBuffer::FillOps(grpc_op* ops, size_t* nops) {
diff --git a/src/cpp/server/server.cc b/src/cpp/server/server.cc
index 80eb488b41d667a9340388cca11cc46dba822d08..dbd88c5b8c1af8ee2a1d782667e3271bd527ede1 100644
--- a/src/cpp/server/server.cc
+++ b/src/cpp/server/server.cc
@@ -52,6 +52,14 @@
 
 namespace grpc {
 
+class Server::ShutdownRequest GRPC_FINAL : public CompletionQueueTag {
+ public:
+  bool FinalizeResult(void** tag, bool* status) {
+    delete this;
+    return false;
+  }
+};
+
 class Server::SyncRequest GRPC_FINAL : public CompletionQueueTag {
  public:
   SyncRequest(RpcServiceMethod* method, void* tag)
@@ -217,6 +225,9 @@ Server::~Server() {
       Shutdown();
     }
   }
+  void* got_tag;
+  bool ok;
+  GPR_ASSERT(!cq_.Next(&got_tag, &ok));
   grpc_server_destroy(server_);
   if (thread_pool_owned_) {
     delete thread_pool_;
@@ -290,7 +301,7 @@ void Server::Shutdown() {
   grpc::unique_lock<grpc::mutex> lock(mu_);
   if (started_ && !shutdown_) {
     shutdown_ = true;
-    grpc_server_shutdown(server_);
+    grpc_server_shutdown_and_notify(server_, cq_.cq(), new ShutdownRequest());
     cq_.Shutdown();
 
     // Wait for running callbacks to finish.
diff --git a/src/cpp/util/status.cc b/src/cpp/util/status.cc
index b694a513e7573eeb47784d6732de4c035b77524e..5bb9eda3d9c898fe2d755165d0053048fc9fdd34 100644
--- a/src/cpp/util/status.cc
+++ b/src/cpp/util/status.cc
@@ -36,6 +36,6 @@
 namespace grpc {
 
 const Status& Status::OK = Status();
-const Status& Status::Cancelled = Status(StatusCode::CANCELLED);
+const Status& Status::CANCELLED = Status(StatusCode::CANCELLED, "");
 
 }  // namespace grpc
diff --git a/src/csharp/Grpc.Core.Tests/ClientServerTest.cs b/src/csharp/Grpc.Core.Tests/ClientServerTest.cs
index 82ded5cc7a6baf20cf1a5ecaf276cb0e5140d139..c09d0b10d4ce984e3e4276cfe26c2db7777e06f0 100644
--- a/src/csharp/Grpc.Core.Tests/ClientServerTest.cs
+++ b/src/csharp/Grpc.Core.Tests/ClientServerTest.cs
@@ -205,20 +205,22 @@ namespace Grpc.Core.Tests
                                        () => { Calls.BlockingUnaryCall(call, "ABC", default(CancellationToken)); });
         }
 
-        [Test]
-        public void UnknownMethodHandler()
-        {
-            var call = new Call<string, string>(ServiceName, NonexistentMethod, channel, Metadata.Empty);
-            try
-            {
-                Calls.BlockingUnaryCall(call, "ABC", default(CancellationToken));
-                Assert.Fail();
-            }
-            catch (RpcException e)
-            {
-                Assert.AreEqual(StatusCode.Unimplemented, e.Status.StatusCode);
-            }
-        }
+//        TODO(jtattermusch): temporarily commented out for #1731
+//                            to be uncommented along with PR #1577
+//        [Test]
+//        public void UnknownMethodHandler()
+//        {
+//            var call = new Call<string, string>(ServiceName, NonexistentMethod, channel, Metadata.Empty);
+//            try
+//            {
+//                Calls.BlockingUnaryCall(call, "ABC", default(CancellationToken));
+//                Assert.Fail();
+//            }
+//            catch (RpcException e)
+//            {
+//                Assert.AreEqual(StatusCode.Unimplemented, e.Status.StatusCode);
+//            }
+//        }
 
         private static async Task<string> EchoHandler(ServerCallContext context, string request)
         {
diff --git a/src/csharp/Grpc.Core/Internal/CallSafeHandle.cs b/src/csharp/Grpc.Core/Internal/CallSafeHandle.cs
index 0651498f0e9a36236d5e4cafbc0bd4d3213c308e..ef92b44402b3568f1cfd24a8cbb197e7e2654849 100644
--- a/src/csharp/Grpc.Core/Internal/CallSafeHandle.cs
+++ b/src/csharp/Grpc.Core/Internal/CallSafeHandle.cs
@@ -192,7 +192,5 @@ namespace Grpc.Core.Internal
         {
             return buffered ? 0 : GRPC_WRITE_BUFFER_HINT;
         }
-
-
     }
 }
\ No newline at end of file
diff --git a/src/csharp/Grpc.Core/Internal/CompletionRegistry.cs b/src/csharp/Grpc.Core/Internal/CompletionRegistry.cs
index 118aa13c5ac1dee38fb3bc0b24e9e16fd52b6f24..80f006ae50da80fbde04206c14320db06e922f8c 100644
--- a/src/csharp/Grpc.Core/Internal/CompletionRegistry.cs
+++ b/src/csharp/Grpc.Core/Internal/CompletionRegistry.cs
@@ -32,14 +32,15 @@
 #endregion
 
 using System;
-using System.Collections.Generic;
 using System.Collections.Concurrent;
+using System.Collections.Generic;
 using System.Runtime.InteropServices;
 using Grpc.Core.Utils;
 
 namespace Grpc.Core.Internal
 {
     internal delegate void OpCompletionDelegate(bool success);
+
     internal delegate void BatchCompletionDelegate(bool success, BatchContextSafeHandle ctx);
 
     internal class CompletionRegistry
diff --git a/src/csharp/Grpc.Core/Internal/ServerSafeHandle.cs b/src/csharp/Grpc.Core/Internal/ServerSafeHandle.cs
index 9fda1f65691a652be4ad8b8e54e40012639f7b1a..83dbb910aa7498a66d7dd5b3d8eee26892fd7d21 100644
--- a/src/csharp/Grpc.Core/Internal/ServerSafeHandle.cs
+++ b/src/csharp/Grpc.Core/Internal/ServerSafeHandle.cs
@@ -60,10 +60,10 @@ namespace Grpc.Core.Internal
         static extern GRPCCallError grpcsharp_server_request_call(ServerSafeHandle server, CompletionQueueSafeHandle cq, BatchContextSafeHandle ctx);
 
         [DllImport("grpc_csharp_ext.dll")]
-        static extern void grpcsharp_server_shutdown(ServerSafeHandle server);
+        static extern void grpcsharp_server_cancel_all_calls(ServerSafeHandle server);
 
         [DllImport("grpc_csharp_ext.dll")]
-        static extern void grpcsharp_server_shutdown_and_notify_callback(ServerSafeHandle server, BatchContextSafeHandle ctx);
+        static extern void grpcsharp_server_shutdown_and_notify_callback(ServerSafeHandle server, CompletionQueueSafeHandle cq, BatchContextSafeHandle ctx);
 
         [DllImport("grpc_csharp_ext.dll")]
         static extern void grpcsharp_server_destroy(IntPtr server);
@@ -91,17 +91,12 @@ namespace Grpc.Core.Internal
         {
             grpcsharp_server_start(this);
         }
-
-        public void Shutdown()
-        {
-            grpcsharp_server_shutdown(this);
-        }
-
-        public void ShutdownAndNotify(BatchCompletionDelegate callback)
+            
+        public void ShutdownAndNotify(CompletionQueueSafeHandle cq, BatchCompletionDelegate callback)
         {
             var ctx = BatchContextSafeHandle.Create();
             GrpcEnvironment.CompletionRegistry.RegisterBatchCompletion(ctx, callback);
-            grpcsharp_server_shutdown_and_notify_callback(this, ctx);
+            grpcsharp_server_shutdown_and_notify_callback(this, cq, ctx);
         }
 
         public void RequestCall(CompletionQueueSafeHandle cq, BatchCompletionDelegate callback)
@@ -116,5 +111,11 @@ namespace Grpc.Core.Internal
             grpcsharp_server_destroy(handle);
             return true;
         }
+            
+        // Only to be called after ShutdownAndNotify.
+        public void CancelAllCalls()
+        {
+            grpcsharp_server_cancel_all_calls(this);
+        }
     }
 }
diff --git a/src/csharp/Grpc.Core/Server.cs b/src/csharp/Grpc.Core/Server.cs
index de10be39abf5885d49c4685cc1169303b1823b6c..8e818885d181a1dc3a3903480b350a728ad7086a 100644
--- a/src/csharp/Grpc.Core/Server.cs
+++ b/src/csharp/Grpc.Core/Server.cs
@@ -143,7 +143,8 @@ namespace Grpc.Core
                 Preconditions.CheckState(!shutdownRequested);
                 shutdownRequested = true;
             }
-            handle.ShutdownAndNotify(HandleServerShutdown);
+
+            handle.ShutdownAndNotify(GetCompletionQueue(), HandleServerShutdown);
             await shutdownTcs.Task;
             handle.Dispose();
         }
@@ -159,8 +160,22 @@ namespace Grpc.Core
             }
         }
 
-        public void Kill()
+        /// <summary>
+        /// Requests server shutdown while cancelling all the in-progress calls.
+        /// The returned task finishes when shutdown procedure is complete.
+        /// </summary>
+        public async Task KillAsync()
         {
+            lock (myLock)
+            {
+                Preconditions.CheckState(startRequested);
+                Preconditions.CheckState(!shutdownRequested);
+                shutdownRequested = true;
+            }
+
+            handle.ShutdownAndNotify(GetCompletionQueue(), HandleServerShutdown);
+            handle.CancelAllCalls();
+            await shutdownTcs.Task;
             handle.Dispose();
         }
 
diff --git a/src/csharp/ext/grpc_csharp_ext.c b/src/csharp/ext/grpc_csharp_ext.c
index f01c6c91831fb27489ab6e530c92f4691d82bf98..ec125db78bfb6ecd6ea1014dfbb06e33906913c9 100644
--- a/src/csharp/ext/grpc_csharp_ext.c
+++ b/src/csharp/ext/grpc_csharp_ext.c
@@ -648,14 +648,15 @@ GPR_EXPORT void GPR_CALLTYPE grpcsharp_server_start(grpc_server *server) {
   grpc_server_start(server);
 }
 
-GPR_EXPORT void GPR_CALLTYPE grpcsharp_server_shutdown(grpc_server *server) {
-  grpc_server_shutdown(server);
-}
-
 GPR_EXPORT void GPR_CALLTYPE
 grpcsharp_server_shutdown_and_notify_callback(grpc_server *server,
+                                              grpc_completion_queue *cq,
                                               grpcsharp_batch_context *ctx) {
-  grpc_server_shutdown_and_notify(server, ctx);
+  grpc_server_shutdown_and_notify(server, cq, ctx);
+}
+
+GPR_EXPORT void GPR_CALLTYPE grpcsharp_server_cancel_all_calls(grpc_server *server) {
+  grpc_server_cancel_all_calls(server);
 }
 
 GPR_EXPORT void GPR_CALLTYPE grpcsharp_server_destroy(grpc_server *server) {
diff --git a/src/node/ext/server.cc b/src/node/ext/server.cc
index eb97f7348b4b5a6fb37bd11d22cbf98f306c72fb..51c55ba96573542da4c459999be768085a7a79c2 100644
--- a/src/node/ext/server.cc
+++ b/src/node/ext/server.cc
@@ -112,9 +112,17 @@ class NewCallOp : public Op {
   }
 };
 
-Server::Server(grpc_server *server) : wrapped_server(server) {}
+Server::Server(grpc_server *server) : wrapped_server(server) {
+  shutdown_queue = grpc_completion_queue_create();
+  grpc_server_register_completion_queue(server, shutdown_queue);
+}
 
-Server::~Server() { grpc_server_destroy(wrapped_server); }
+Server::~Server() {
+  this->ShutdownServer();
+  grpc_completion_queue_shutdown(this->shutdown_queue);
+  grpc_server_destroy(wrapped_server);
+  grpc_completion_queue_destroy(this->shutdown_queue);
+}
 
 void Server::Init(Handle<Object> exports) {
   NanScope();
@@ -148,6 +156,16 @@ bool Server::HasInstance(Handle<Value> val) {
   return NanHasInstance(fun_tpl, val);
 }
 
+void Server::ShutdownServer() {
+  if (this->wrapped_server != NULL) {
+    grpc_server_shutdown_and_notify(this->wrapped_server,
+                                    this->shutdown_queue,
+                                    NULL);
+    grpc_completion_queue_pluck(this->shutdown_queue, NULL, gpr_inf_future);
+    this->wrapped_server = NULL;
+  }
+}
+
 NAN_METHOD(Server::New) {
   NanScope();
 
@@ -207,6 +225,9 @@ NAN_METHOD(Server::RequestCall) {
     return NanThrowTypeError("requestCall can only be called on a Server");
   }
   Server *server = ObjectWrap::Unwrap<Server>(args.This());
+  if (server->wrapped_server == NULL) {
+    return NanThrowError("requestCall cannot be called on a shut down Server");
+  }
   NewCallOp *op = new NewCallOp();
   unique_ptr<OpVec> ops(new OpVec());
   ops->push_back(unique_ptr<Op>(op));
@@ -232,6 +253,9 @@ NAN_METHOD(Server::AddHttp2Port) {
     return NanThrowTypeError("addHttp2Port's argument must be a String");
   }
   Server *server = ObjectWrap::Unwrap<Server>(args.This());
+  if (server->wrapped_server == NULL) {
+    return NanThrowError("addHttp2Port cannot be called on a shut down Server");
+  }
   NanReturnValue(NanNew<Number>(grpc_server_add_http2_port(
       server->wrapped_server, *NanUtf8String(args[0]))));
 }
@@ -251,6 +275,10 @@ NAN_METHOD(Server::AddSecureHttp2Port) {
         "addSecureHttp2Port's second argument must be ServerCredentials");
   }
   Server *server = ObjectWrap::Unwrap<Server>(args.This());
+  if (server->wrapped_server == NULL) {
+    return NanThrowError(
+        "addSecureHttp2Port cannot be called on a shut down Server");
+  }
   ServerCredentials *creds = ObjectWrap::Unwrap<ServerCredentials>(
       args[1]->ToObject());
   NanReturnValue(NanNew<Number>(grpc_server_add_secure_http2_port(
@@ -264,17 +292,24 @@ NAN_METHOD(Server::Start) {
     return NanThrowTypeError("start can only be called on a Server");
   }
   Server *server = ObjectWrap::Unwrap<Server>(args.This());
+  if (server->wrapped_server == NULL) {
+    return NanThrowError("start cannot be called on a shut down Server");
+  }
   grpc_server_start(server->wrapped_server);
   NanReturnUndefined();
 }
 
+NAN_METHOD(ShutdownCallback) {
+  NanReturnUndefined();
+}
+
 NAN_METHOD(Server::Shutdown) {
   NanScope();
   if (!HasInstance(args.This())) {
     return NanThrowTypeError("shutdown can only be called on a Server");
   }
   Server *server = ObjectWrap::Unwrap<Server>(args.This());
-  grpc_server_shutdown(server->wrapped_server);
+  server->ShutdownServer();
   NanReturnUndefined();
 }
 
diff --git a/src/node/ext/server.h b/src/node/ext/server.h
index 641d5ccb3e4f7cc5acaba05e22f946336f04e801..5b4b18a0e0901c80c2c2c8d8cca3bafc2576c454 100644
--- a/src/node/ext/server.h
+++ b/src/node/ext/server.h
@@ -61,6 +61,8 @@ class Server : public ::node::ObjectWrap {
   Server(const Server &);
   Server &operator=(const Server &);
 
+  void ShutdownServer();
+
   static NAN_METHOD(New);
   static NAN_METHOD(RequestCall);
   static NAN_METHOD(AddHttp2Port);
@@ -71,6 +73,7 @@ class Server : public ::node::ObjectWrap {
   static v8::Persistent<v8::FunctionTemplate> fun_tpl;
 
   grpc_server *wrapped_server;
+  grpc_completion_queue *shutdown_queue;
 };
 
 }  // namespace node
diff --git a/src/objective-c/README.md b/src/objective-c/README.md
index 0e5eb9a25f666869c2d4f2d46a1d861e8234be34..e997b76d14ba145b806ee0c5e6ff267fd0e83939 100644
--- a/src/objective-c/README.md
+++ b/src/objective-c/README.md
@@ -163,7 +163,7 @@ files:
 
 * [Podspec](https://github.com/grpc/grpc/blob/master/gRPC.podspec) for the Objective-C gRPC runtime
 library. This can be tedious to configure manually.
-* [Podspec](https://github.com/jcanizales/protobuf/blob/add-podspec/Protobuf.podspec) for the
+* [Podspec](https://github.com/google/protobuf/blob/master/Protobuf.podspec) for the
 Objective-C Protobuf runtime library.
 
 [Protocol Buffers]:https://developers.google.com/protocol-buffers/
diff --git a/src/php/ext/grpc/server.c b/src/php/ext/grpc/server.c
index b7995b6b8bd42bba66a7934bebbe818454cabc18..02c886c715ce074702e12ca838491bc72e4aa112 100644
--- a/src/php/ext/grpc/server.c
+++ b/src/php/ext/grpc/server.c
@@ -63,7 +63,8 @@ zend_class_entry *grpc_ce_server;
 void free_wrapped_grpc_server(void *object TSRMLS_DC) {
   wrapped_grpc_server *server = (wrapped_grpc_server *)object;
   if (server->wrapped != NULL) {
-    grpc_server_shutdown(server->wrapped);
+    grpc_server_shutdown_and_notify(server->wrapped, completion_queue, NULL);
+    grpc_completion_queue_pluck(completion_queue, NULL, gpr_inf_future);
     grpc_server_destroy(server->wrapped);
   }
   efree(server);
diff --git a/src/python/src/grpc/_adapter/_c/types/server.c b/src/python/src/grpc/_adapter/_c/types/server.c
index 65d84b58fe8e3539f39f2124d2dfbaf6755fa26e..26b38da8f1a0e08d9d78e4dd6d9fa9b59ae7d69a 100644
--- a/src/python/src/grpc/_adapter/_c/types/server.c
+++ b/src/python/src/grpc/_adapter/_c/types/server.c
@@ -167,17 +167,13 @@ PyObject *pygrpc_Server_start(Server *self, PyObject *ignored) {
 
 PyObject *pygrpc_Server_shutdown(
     Server *self, PyObject *args, PyObject *kwargs) {
-  PyObject *user_tag = NULL;
+  PyObject *user_tag;
   pygrpc_tag *tag;
   static char *keywords[] = {"tag", NULL};
-  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|O", keywords, &user_tag)) {
+  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O", keywords, &user_tag)) {
     return NULL;
   }
-  if (user_tag) {
-    tag = pygrpc_produce_server_shutdown_tag(user_tag);
-    grpc_server_shutdown_and_notify(self->c_serv, tag);
-  } else {
-    grpc_server_shutdown(self->c_serv);
-  }
+  tag = pygrpc_produce_server_shutdown_tag(user_tag);
+  grpc_server_shutdown_and_notify(self->c_serv, self->cq->c_cq, tag);
   Py_RETURN_NONE;
 }
diff --git a/src/python/src/grpc/_adapter/_c/utility.c b/src/python/src/grpc/_adapter/_c/utility.c
index 64b3428f2b14aefb043ac7da512a47b9e15aeaeb..a433f26d769163ae0145f5f6017279196872cf49 100644
--- a/src/python/src/grpc/_adapter/_c/utility.c
+++ b/src/python/src/grpc/_adapter/_c/utility.c
@@ -123,7 +123,8 @@ PyObject *pygrpc_consume_event(grpc_event event) {
           event.success ? Py_True : Py_False);
     } else {
       result = Py_BuildValue("iOOONO", GRPC_OP_COMPLETE, tag->user_tag,
-          tag->call, Py_None, pygrpc_consume_ops(tag->ops, tag->nops),
+          tag->call ? (PyObject*)tag->call : Py_None, Py_None,
+          pygrpc_consume_ops(tag->ops, tag->nops),
           event.success ? Py_True : Py_False);
     }
     break;
@@ -196,10 +197,11 @@ int pygrpc_produce_op(PyObject *op, grpc_op *result) {
       return 0;
     }
     if (!PyTuple_Check(PyTuple_GET_ITEM(op, STATUS_INDEX))) {
-      char buf[64];
-      snprintf(buf, sizeof(buf), "expected tuple status in op of length %d",
-               STATUS_TUPLE_SIZE);
-      PyErr_SetString(PyExc_TypeError, buf);
+      char *buf;
+      gpr_asprintf(&buf, "expected tuple status in op of length %d",
+                   STATUS_TUPLE_SIZE);
+      PyErr_SetString(PyExc_ValueError, buf);
+      gpr_free(buf);
       return 0;
     }
     c_op.data.send_status_from_server.status = PyInt_AsLong(
diff --git a/src/python/src/grpc/_adapter/_intermediary_low.py b/src/python/src/grpc/_adapter/_intermediary_low.py
index a6e325c4e5b63e62b029ea53c8b50057647cd6ba..6b96aef1d34e0a99c309b50dc17bfd98060b1645 100644
--- a/src/python/src/grpc/_adapter/_intermediary_low.py
+++ b/src/python/src/grpc/_adapter/_intermediary_low.py
@@ -100,7 +100,7 @@ class _TagAdapter(collections.namedtuple('_TagAdapter', [
 
 class Call(object):
   """Adapter from old _low.Call interface to new _low.Call."""
-  
+
   def __init__(self, channel, completion_queue, method, host, deadline):
     self._internal = channel._internal.create_call(
         completion_queue._internal, method, host, deadline)
@@ -207,7 +207,7 @@ class CompletionQueue(object):
       complete_accepted = ev.success if kind == Event.Kind.COMPLETE_ACCEPTED else None
       service_acceptance = ServiceAcceptance(Call._from_internal(ev.call), ev.call_details.method, ev.call_details.host, ev.call_details.deadline) if kind == Event.Kind.SERVICE_ACCEPTED else None
       message_bytes = ev.results[0].message if kind == Event.Kind.READ_ACCEPTED else None
-      status = Status(ev.results[0].status.code, ev.results[0].status.details) if (kind == Event.Kind.FINISH and ev.results[0].status) else Status(_types.StatusCode.CANCELLED if ev.results[0].cancelled else _types.StatusCode.OK, '') if ev.results[0].cancelled is not None else None
+      status = Status(ev.results[0].status.code, ev.results[0].status.details) if (kind == Event.Kind.FINISH and ev.results[0].status) else Status(_types.StatusCode.CANCELLED if ev.results[0].cancelled else _types.StatusCode.OK, '') if len(ev.results) > 0 and ev.results[0].cancelled is not None else None
       metadata = ev.results[0].initial_metadata if (kind in [Event.Kind.SERVICE_ACCEPTED, Event.Kind.METADATA_ACCEPTED]) else (ev.results[0].trailing_metadata if kind == Event.Kind.FINISH else None)
     else:
       raise RuntimeError('unknown event')
@@ -241,7 +241,7 @@ class Server(object):
     return self._internal.request_call(self._internal_cq, _TagAdapter(tag, Event.Kind.SERVICE_ACCEPTED))
 
   def stop(self):
-    return self._internal.shutdown()
+    return self._internal.shutdown(_TagAdapter(None, Event.Kind.STOP))
 
 
 class ClientCredentials(object):
@@ -253,6 +253,6 @@ class ClientCredentials(object):
 
 class ServerCredentials(object):
   """Adapter from old _low.ServerCredentials interface to new _low.ServerCredentials."""
-  
+
   def __init__(self, root_credentials, pair_sequence):
     self._internal = _low.ServerCredentials.ssl(root_credentials, list(pair_sequence))
diff --git a/src/python/src/grpc/_adapter/_intermediary_low_test.py b/src/python/src/grpc/_adapter/_intermediary_low_test.py
index 6ff51c43a69ae02ade3de7c6aac228beef6fed65..478346341b08d62a70dffb82bea474f037bcf249 100644
--- a/src/python/src/grpc/_adapter/_intermediary_low_test.py
+++ b/src/python/src/grpc/_adapter/_intermediary_low_test.py
@@ -94,14 +94,6 @@ class EchoTest(unittest.TestCase):
 
   def tearDown(self):
     self.server.stop()
-    # NOTE(nathaniel): Yep, this is weird; it's a consequence of
-    # grpc_server_destroy's being what has the effect of telling the server's
-    # completion queue to pump out all pending events/tags immediately rather
-    # than gracefully completing all outstanding RPCs while accepting no new
-    # ones.
-    # TODO(nathaniel): Deallocation of a Python object shouldn't have this kind
-    # of observable side effect let alone such an important one.
-    del self.server
     self.server_completion_queue.stop()
     self.client_completion_queue.stop()
     while True:
@@ -114,6 +106,7 @@ class EchoTest(unittest.TestCase):
         break
     self.server_completion_queue = None
     self.client_completion_queue = None
+    del self.server
 
   def _perform_echo_test(self, test_data):
     method = 'test method'
@@ -316,7 +309,6 @@ class CancellationTest(unittest.TestCase):
 
   def tearDown(self):
     self.server.stop()
-    del self.server
     self.server_completion_queue.stop()
     self.client_completion_queue.stop()
     while True:
@@ -327,6 +319,7 @@ class CancellationTest(unittest.TestCase):
       event = self.client_completion_queue.get(0)
       if event is not None and event.kind is _low.Event.Kind.STOP:
         break
+    del self.server
 
   def testCancellation(self):
     method = 'test method'
diff --git a/src/python/src/grpc/_adapter/_low.py b/src/python/src/grpc/_adapter/_low.py
index 0c1d3b40a5b2df1f8e8f6c8e36e59bde0849b2c8..dcf67dbc11732f5abc9dd7ea31b14ccc042b6ed2 100644
--- a/src/python/src/grpc/_adapter/_low.py
+++ b/src/python/src/grpc/_adapter/_low.py
@@ -101,11 +101,8 @@ class Server(_types.Server):
   def start(self):
     return self.server.start()
 
-  def shutdown(self, tag=_NO_TAG):
-    if tag is _NO_TAG:
-      return self.server.shutdown()
-    else:
-      return self.server.shutdown(tag)
+  def shutdown(self, tag=None):
+    return self.server.shutdown(tag)
 
   def request_call(self, completion_queue, tag):
     return self.server.request_call(completion_queue.completion_queue, tag)
diff --git a/src/python/src/grpc/_adapter/_low_test.py b/src/python/src/grpc/_adapter/_low_test.py
index e53b176caf95562d69379a128cd4a74c0a082d11..8a9f1a0c4983e4bc0c6e985db69802c282cb3b87 100644
--- a/src/python/src/grpc/_adapter/_low_test.py
+++ b/src/python/src/grpc/_adapter/_low_test.py
@@ -48,7 +48,6 @@ class InsecureServerInsecureClient(unittest.TestCase):
   def tearDown(self):
     self.server.shutdown()
     del self.client_channel
-    del self.server
 
     self.client_completion_queue.shutdown()
     while self.client_completion_queue.next().type != _types.EventType.QUEUE_SHUTDOWN:
@@ -59,6 +58,7 @@ class InsecureServerInsecureClient(unittest.TestCase):
 
     del self.client_completion_queue
     del self.server_completion_queue
+    del self.server
 
   def testEcho(self):
     DEADLINE = time.time()+5
diff --git a/src/ruby/ext/grpc/rb_completion_queue.c b/src/ruby/ext/grpc/rb_completion_queue.c
index fa4c5660048fed2bcbb77b5b058b2bcefe3883e2..8fb3949b3dc7cf8a2eff4770e92e9480463d1a7b 100644
--- a/src/ruby/ext/grpc/rb_completion_queue.c
+++ b/src/ruby/ext/grpc/rb_completion_queue.c
@@ -142,8 +142,16 @@ grpc_event grpc_rb_completion_queue_pluck_event(VALUE self, VALUE tag,
   MEMZERO(&next_call, next_call_stack, 1);
   TypedData_Get_Struct(self, grpc_completion_queue,
                        &grpc_rb_completion_queue_data_type, next_call.cq);
-  next_call.timeout = grpc_rb_time_timeval(timeout, /* absolute time*/ 0);
-  next_call.tag = ROBJECT(tag);
+  if (TYPE(timeout) == T_NIL) {
+    next_call.timeout = gpr_inf_future;
+  } else {
+    next_call.timeout = grpc_rb_time_timeval(timeout, /* absolute time*/ 0);
+  }
+  if (TYPE(tag) == T_NIL) {
+    next_call.tag = NULL;
+  } else {
+    next_call.tag = ROBJECT(tag);
+  }
   next_call.event.type = GRPC_QUEUE_TIMEOUT;
   rb_thread_call_without_gvl(grpc_rb_completion_queue_pluck_no_gil,
                              (void *)&next_call, NULL, NULL);
diff --git a/src/ruby/ext/grpc/rb_server.c b/src/ruby/ext/grpc/rb_server.c
index 837ca3b5e8db8fdb1d0ca8a1da02789a601fda50..9c0d24bf8fd47ce1fafe9d885a4fd4be308f7e8f 100644
--- a/src/ruby/ext/grpc/rb_server.c
+++ b/src/ruby/ext/grpc/rb_server.c
@@ -210,7 +210,7 @@ static VALUE grpc_rb_server_request_call(VALUE self, VALUE cqueue,
   VALUE result;
   TypedData_Get_Struct(self, grpc_rb_server, &grpc_rb_server_data_type, s);
   if (s->wrapped == NULL) {
-    rb_raise(rb_eRuntimeError, "closed!");
+    rb_raise(rb_eRuntimeError, "destroyed!");
     return Qnil;
   } else {
     grpc_request_call_stack_init(&st);
@@ -259,21 +259,69 @@ static VALUE grpc_rb_server_start(VALUE self) {
   grpc_rb_server *s = NULL;
   TypedData_Get_Struct(self, grpc_rb_server, &grpc_rb_server_data_type, s);
   if (s->wrapped == NULL) {
-    rb_raise(rb_eRuntimeError, "closed!");
+    rb_raise(rb_eRuntimeError, "destroyed!");
   } else {
     grpc_server_start(s->wrapped);
   }
   return Qnil;
 }
 
-static VALUE grpc_rb_server_destroy(VALUE self) {
+/*
+  call-seq:
+    cq = CompletionQueue.new
+    server = Server.new(cq, {'arg1': 'value1'})
+    ... // do stuff with server
+    ...
+    ... // to shutdown the server
+    server.destroy(cq)
+
+    ... // to shutdown the server with a timeout
+    server.destroy(cq, timeout)
+
+  Destroys server instances. */
+static VALUE grpc_rb_server_destroy(int argc, VALUE *argv, VALUE self) {
+  VALUE cqueue = Qnil;
+  VALUE timeout = Qnil;
+  grpc_completion_queue *cq = NULL;
+  grpc_event ev;
   grpc_rb_server *s = NULL;
+
+  /* "11" == 1 mandatory args, 1 (timeout) is optional */
+  rb_scan_args(argc, argv, "11", &cqueue, &timeout);
+  cq = grpc_rb_get_wrapped_completion_queue(cqueue);
   TypedData_Get_Struct(self, grpc_rb_server, &grpc_rb_server_data_type, s);
+
   if (s->wrapped != NULL) {
-    grpc_server_shutdown(s->wrapped);
+    grpc_server_shutdown_and_notify(s->wrapped, cq, NULL);
+    ev = grpc_rb_completion_queue_pluck_event(cqueue, Qnil, timeout);
+
+    if (!ev.success) {
+      rb_warn("server shutdown failed, there will be a LEAKED object warning");
+      return Qnil;
+      /*
+         TODO: renable the rb_raise below.
+
+         At the moment if the timeout is INFINITE_FUTURE as recommended, the
+         pluck blocks forever, even though
+
+         the outstanding server_request_calls correctly fail on the other
+         thread that they are running on.
+
+         it's almost as if calls that fail on the other thread do not get
+         cleaned up by shutdown request, even though it caused htem to
+         terminate.
+
+         rb_raise(rb_eRuntimeError, "grpc server shutdown did not succeed");
+         return Qnil;
+
+         The workaround is just to use a timeout and return without really
+         shutting down the server, and rely on the grpc core garbage collection
+         it down as a 'LEAKED OBJECT'.
+
+      */
+    }
     grpc_server_destroy(s->wrapped);
     s->wrapped = NULL;
-    s->mark = Qnil;
   }
   return Qnil;
 }
@@ -302,7 +350,7 @@ static VALUE grpc_rb_server_add_http2_port(int argc, VALUE *argv, VALUE self) {
 
   TypedData_Get_Struct(self, grpc_rb_server, &grpc_rb_server_data_type, s);
   if (s->wrapped == NULL) {
-    rb_raise(rb_eRuntimeError, "closed!");
+    rb_raise(rb_eRuntimeError, "destroyed!");
     return Qnil;
   } else if (rb_creds == Qnil) {
     recvd_port = grpc_server_add_http2_port(s->wrapped, StringValueCStr(port));
@@ -315,7 +363,7 @@ static VALUE grpc_rb_server_add_http2_port(int argc, VALUE *argv, VALUE self) {
     creds = grpc_rb_get_wrapped_server_credentials(rb_creds);
     recvd_port =
         grpc_server_add_secure_http2_port(s->wrapped, StringValueCStr(port),
-			                  creds);
+                                          creds);
     if (recvd_port == 0) {
       rb_raise(rb_eRuntimeError,
                "could not add secure port %s to server, not sure why",
@@ -341,7 +389,7 @@ void Init_grpc_server() {
   rb_define_method(grpc_rb_cServer, "request_call",
                    grpc_rb_server_request_call, 3);
   rb_define_method(grpc_rb_cServer, "start", grpc_rb_server_start, 0);
-  rb_define_method(grpc_rb_cServer, "destroy", grpc_rb_server_destroy, 0);
+  rb_define_method(grpc_rb_cServer, "destroy", grpc_rb_server_destroy, -1);
   rb_define_alias(grpc_rb_cServer, "close", "destroy");
   rb_define_method(grpc_rb_cServer, "add_http2_port",
                    grpc_rb_server_add_http2_port,
diff --git a/src/ruby/lib/grpc/generic/rpc_server.rb b/src/ruby/lib/grpc/generic/rpc_server.rb
index dcb11bfbef7abe2eaf064da383a292f3ce965c15..a7e20d6b82c63067de2178c6f9172cbef014f151 100644
--- a/src/ruby/lib/grpc/generic/rpc_server.rb
+++ b/src/ruby/lib/grpc/generic/rpc_server.rb
@@ -278,7 +278,9 @@ module GRPC
         @stopped = true
       end
       @pool.stop
-      @server.close
+      deadline = from_relative_time(@poll_period)
+
+      @server.close(@cq, deadline)
     end
 
     # determines if the server has been stopped
@@ -410,17 +412,18 @@ module GRPC
     # handles calls to the server
     def loop_handle_server_calls
       fail 'not running' unless @running
-      request_call_tag = Object.new
+      loop_tag = Object.new
       until stopped?
         deadline = from_relative_time(@poll_period)
         begin
-          an_rpc = @server.request_call(@cq, request_call_tag, deadline)
+          an_rpc = @server.request_call(@cq, loop_tag, deadline)
+          c = new_active_server_call(an_rpc)
         rescue Core::CallError, RuntimeError => e
-          # can happen during server shutdown
+          # these might happen for various reasonse.  The correct behaviour of
+          # the server is to log them and continue.
           GRPC.logger.warn("server call failed: #{e}")
           next
         end
-        c = new_active_server_call(an_rpc)
         unless c.nil?
           mth = an_rpc.method.to_sym
           @pool.schedule(c) do |call|
diff --git a/src/ruby/spec/client_server_spec.rb b/src/ruby/spec/client_server_spec.rb
index 68af79f9075591485ba18f8345d61e506c6842b1..b22e510a6a24fbd4bbb773791023bdeef9eadb3d 100644
--- a/src/ruby/spec/client_server_spec.rb
+++ b/src/ruby/spec/client_server_spec.rb
@@ -42,11 +42,8 @@ shared_context 'setup: tags' do
   let(:sent_message) { 'sent message' }
   let(:reply_text) { 'the reply' }
   before(:example) do
-    @server_finished_tag = Object.new
-    @client_finished_tag = Object.new
-    @client_metadata_tag = Object.new
+    @client_tag = Object.new
     @server_tag = Object.new
-    @tag = Object.new
   end
 
   def deadline
@@ -351,7 +348,7 @@ describe 'the http client/server' do
 
   after(:example) do
     @ch.close
-    @server.close
+    @server.close(@server_queue, deadline)
   end
 
   it_behaves_like 'basic GRPC message delivery is OK' do
@@ -377,7 +374,7 @@ describe 'the secure http client/server' do
   end
 
   after(:example) do
-    @server.close
+    @server.close(@server_queue, deadline)
   end
 
   it_behaves_like 'basic GRPC message delivery is OK' do
diff --git a/src/ruby/spec/generic/active_call_spec.rb b/src/ruby/spec/generic/active_call_spec.rb
index 575871afb110aa1de18dad5c34ec6c2fc215c3be..bc3bee3d440b3172944408e02e59f7ff4527cbc7 100644
--- a/src/ruby/spec/generic/active_call_spec.rb
+++ b/src/ruby/spec/generic/active_call_spec.rb
@@ -51,7 +51,7 @@ describe GRPC::ActiveCall do
   end
 
   after(:each) do
-    @server.close
+    @server.close(@server_queue, deadline)
   end
 
   describe 'restricted view methods' do
diff --git a/src/ruby/spec/generic/client_stub_spec.rb b/src/ruby/spec/generic/client_stub_spec.rb
index 98d68ccfbb89d775f8304700d3d806bc00fdc6cd..68d4b117905c44ad7edff98c377999f9bdec3233 100644
--- a/src/ruby/spec/generic/client_stub_spec.rb
+++ b/src/ruby/spec/generic/client_stub_spec.rb
@@ -54,6 +54,7 @@ describe 'ClientStub' do
   before(:each) do
     Thread.abort_on_exception = true
     @server = nil
+    @server_queue = nil
     @method = 'an_rpc_method'
     @pass = OK
     @fail = INTERNAL
@@ -61,7 +62,7 @@ describe 'ClientStub' do
   end
 
   after(:each) do
-    @server.close unless @server.nil?
+    @server.close(@server_queue) unless @server_queue.nil?
   end
 
   describe '#new' do
diff --git a/src/ruby/spec/generic/rpc_server_spec.rb b/src/ruby/spec/generic/rpc_server_spec.rb
index e60a8b27c3fd672a8d0217002a57ff8474b47baf..f2403de77c4bf705a4e77e6f9e191ce7f31a8ca4 100644
--- a/src/ruby/spec/generic/rpc_server_spec.rb
+++ b/src/ruby/spec/generic/rpc_server_spec.rb
@@ -136,10 +136,6 @@ describe GRPC::RpcServer do
     @ch = GRPC::Core::Channel.new(@host, nil)
   end
 
-  after(:each) do
-    @server.close
-  end
-
   describe '#new' do
     it 'can be created with just some args' do
       opts = { a_channel_arg: 'an_arg' }
@@ -344,10 +340,6 @@ describe GRPC::RpcServer do
         @srv = RpcServer.new(**server_opts)
       end
 
-      after(:each) do
-        @srv.stop
-      end
-
       it 'should return NOT_FOUND status on unknown methods', server: true do
         @srv.handle(EchoService)
         t = Thread.new { @srv.run }
@@ -527,10 +519,6 @@ describe GRPC::RpcServer do
         @srv = RpcServer.new(**server_opts)
       end
 
-      after(:each) do
-        @srv.stop
-      end
-
       it 'should send connect metadata to the client', server: true do
         service = EchoService.new
         @srv.handle(service)
diff --git a/src/ruby/spec/server_spec.rb b/src/ruby/spec/server_spec.rb
index bb566d1b1fb6e68c8b1fc367b053bd8e0ccbbae9..47fe575343841d7ff4f4bb67123af383a6e2d672 100644
--- a/src/ruby/spec/server_spec.rb
+++ b/src/ruby/spec/server_spec.rb
@@ -54,7 +54,7 @@ describe Server do
 
     it 'fails if the server is closed' do
       s = Server.new(@cq, nil)
-      s.close
+      s.close(@cq)
       expect { s.start }.to raise_error(RuntimeError)
     end
   end
@@ -62,19 +62,19 @@ describe Server do
   describe '#destroy' do
     it 'destroys a server ok' do
       s = start_a_server
-      blk = proc { s.destroy }
+      blk = proc { s.destroy(@cq) }
       expect(&blk).to_not raise_error
     end
 
     it 'can be called more than once without error' do
       s = start_a_server
       begin
-        blk = proc { s.destroy }
+        blk = proc { s.destroy(@cq) }
         expect(&blk).to_not raise_error
         blk.call
         expect(&blk).to_not raise_error
       ensure
-        s.close
+        s.close(@cq)
       end
     end
   end
@@ -83,16 +83,16 @@ describe Server do
     it 'closes a server ok' do
       s = start_a_server
       begin
-        blk = proc { s.close }
+        blk = proc { s.close(@cq) }
         expect(&blk).to_not raise_error
       ensure
-        s.close
+        s.close(@cq)
       end
     end
 
     it 'can be called more than once without error' do
       s = start_a_server
-      blk = proc { s.close }
+      blk = proc { s.close(@cq) }
       expect(&blk).to_not raise_error
       blk.call
       expect(&blk).to_not raise_error
@@ -105,14 +105,14 @@ describe Server do
         blk = proc do
           s = Server.new(@cq, nil)
           s.add_http2_port('localhost:0')
-          s.close
+          s.close(@cq)
         end
         expect(&blk).to_not raise_error
       end
 
       it 'fails if the server is closed' do
         s = Server.new(@cq, nil)
-        s.close
+        s.close(@cq)
         expect { s.add_http2_port('localhost:0') }.to raise_error(RuntimeError)
       end
     end
@@ -123,14 +123,14 @@ describe Server do
         blk = proc do
           s = Server.new(@cq, nil)
           s.add_http2_port('localhost:0', cert)
-          s.close
+          s.close(@cq)
         end
         expect(&blk).to_not raise_error
       end
 
       it 'fails if the server is closed' do
         s = Server.new(@cq, nil)
-        s.close
+        s.close(@cq)
         blk = proc { s.add_http2_port('localhost:0', cert) }
         expect(&blk).to raise_error(RuntimeError)
       end
diff --git a/templates/gRPC.podspec.template b/templates/gRPC.podspec.template
new file mode 100644
index 0000000000000000000000000000000000000000..84716c81e1e3a05ec116c9f13cbb29693054ae8e
--- /dev/null
+++ b/templates/gRPC.podspec.template
@@ -0,0 +1,127 @@
+<%!
+bad_header_names = ('time.h', 'string.h')
+def fix_header_name(name):
+  split_name = name.split('/')
+  if split_name[-1] in bad_header_names:
+    return '/'.join(split_name[:-1] + ['grpc_' + split_name[-1]])
+  else:
+    return name
+%>
+
+Pod::Spec.new do |s|
+  s.name     = 'gRPC'
+  s.version  = '0.6.0'
+  s.summary  = 'gRPC client library for iOS/OSX'
+  s.homepage = 'http://www.grpc.io'
+  s.license  = 'New BSD'
+  s.authors  = { 'The gRPC contributors' => 'grpc-packages@google.com' }
+
+  # s.source = { :git => 'https://github.com/grpc/grpc.git',
+  #              :tag => 'release-0_9_1-objectivec-0.5.1' }
+
+  s.ios.deployment_target = '6.0'
+  s.osx.deployment_target = '10.8'
+  s.requires_arc = true
+
+  # Reactive Extensions library for iOS.
+  s.subspec 'RxLibrary' do |rs|
+    rs.source_files = 'src/objective-c/RxLibrary/*.{h,m}',
+                      'src/objective-c/RxLibrary/transformations/*.{h,m}',
+                      'src/objective-c/RxLibrary/private/*.{h,m}'
+    rs.private_header_files = 'src/objective-c/RxLibrary/private/*.h'
+  end
+
+  # Core cross-platform gRPC library, written in C.
+  s.subspec 'C-Core' do |cs|
+    cs.source_files = \
+% for lib in libs:
+% if lib.name in ("grpc", "gpr"):
+% for hdr in lib.get("headers", []):
+'${fix_header_name(hdr)}', \
+% endfor
+% for hdr in lib.get("public_headers", []):
+'${fix_header_name(hdr)}', \
+% endfor
+% for src in lib.src:
+'${src}', \
+% endfor
+% endif
+% endfor
+
+    cs.private_header_files = \
+% for lib in libs:
+% if lib.name in ("grpc", "gpr"):
+% for hdr in lib.get("headers", []):
+'${hdr}', \
+% endfor
+% endif
+% endfor
+
+    cs.header_mappings_dir = '.'
+    # The core library includes its headers as either "src/core/..." or "grpc/...", meaning we have
+    # to tell XCode to look for headers under the "include" subdirectory too.
+    #
+    # TODO(jcanizales): Instead of doing this, during installation move everything under
+    # "include/grpc" one directory up. The directory names under PODS_ROOT are implementation
+    # details of Cocoapods, and have changed in the past, breaking this podspec.
+    cs.xcconfig = { 'HEADER_SEARCH_PATHS' => '"$(PODS_ROOT)/Headers/Private/gRPC" ' +
+                                             '"$(PODS_ROOT)/Headers/Private/gRPC/include"' }
+    cs.compiler_flags = '-GCC_WARN_INHIBIT_ALL_WARNINGS', '-w'
+
+    cs.requires_arc = false
+    cs.libraries = 'z'
+    cs.dependency 'OpenSSL', '~> 1.0.200'
+  end
+
+  # This is a workaround for Cocoapods Issue #1437.
+  # It renames time.h and string.h to grpc_time.h and grpc_string.h.
+  # It needs to be here (top-level) instead of in the C-Core subspec because Cocoapods doesn't run
+  # prepare_command's of subspecs.
+  #
+  # TODO(jcanizales): Try out Todd Reed's solution at Issue #1437.
+  s.prepare_command = <<-CMD
+    DIR_TIME="grpc/support"
+    BAD_TIME="$DIR_TIME/time.h"
+    GOOD_TIME="$DIR_TIME/grpc_time.h"
+    grep -rl "$BAD_TIME" include/grpc src/core | xargs sed -i '' -e s@$BAD_TIME@$GOOD_TIME@g
+    if [ -f "include/$BAD_TIME" ];
+    then
+      mv -f "include/$BAD_TIME" "include/$GOOD_TIME"
+    fi
+
+    DIR_STRING="src/core/support"
+    BAD_STRING="$DIR_STRING/string.h"
+    GOOD_STRING="$DIR_STRING/grpc_string.h"
+    grep -rl "$BAD_STRING" include/grpc src/core | xargs sed -i '' -e s@$BAD_STRING@$GOOD_STRING@g
+    if [ -f "$BAD_STRING" ];
+    then
+      mv -f "$BAD_STRING" "$GOOD_STRING"
+    fi
+  CMD
+
+  # Objective-C wrapper around the core gRPC library.
+  s.subspec 'GRPCClient' do |gs|
+    gs.source_files = 'src/objective-c/GRPCClient/*.{h,m}',
+                      'src/objective-c/GRPCClient/private/*.{h,m}'
+    gs.private_header_files = 'src/objective-c/GRPCClient/private/*.h'
+    gs.compiler_flags = '-GCC_WARN_INHIBIT_ALL_WARNINGS', '-w'
+
+    gs.dependency 'gRPC/C-Core'
+    # TODO(jcanizales): Remove this when the prepare_command moves everything under "include/grpc"
+    # one directory up.
+    gs.xcconfig = { 'HEADER_SEARCH_PATHS' => '"$(PODS_ROOT)/Headers/Public/gRPC/include"' }
+    gs.dependency 'gRPC/RxLibrary'
+
+    # Certificates, to be able to establish TLS connections:
+    gs.resource_bundles = { 'gRPC' => ['etc/roots.pem'] }
+  end
+
+  # RPC library for ProtocolBuffers, based on gRPC
+  s.subspec 'ProtoRPC' do |ps|
+    ps.source_files = 'src/objective-c/ProtoRPC/*.{h,m}'
+
+    ps.dependency 'gRPC/GRPCClient'
+    ps.dependency 'gRPC/RxLibrary'
+    ps.dependency 'Protobuf', '~> 3.0.0-alpha-3'
+  end
+end
diff --git a/test/core/bad_client/bad_client.c b/test/core/bad_client/bad_client.c
index 7319545c84ccea17cdca32a3d4262d51b3476022..e9adcf34c79c01dfd1dc123db364d2f9e4d11fef 100644
--- a/test/core/bad_client/bad_client.c
+++ b/test/core/bad_client/bad_client.c
@@ -143,6 +143,10 @@ void grpc_run_bad_client_test(grpc_bad_client_server_side_validator validator,
   if (sfd.client) {
     grpc_endpoint_destroy(sfd.client);
   }
+  grpc_server_shutdown_and_notify(a.server, a.cq, NULL);
+  GPR_ASSERT(grpc_completion_queue_pluck(a.cq, NULL,
+                                         GRPC_TIMEOUT_SECONDS_TO_DEADLINE(1))
+                 .type == GRPC_OP_COMPLETE);
   grpc_server_destroy(a.server);
   grpc_completion_queue_destroy(a.cq);
 
diff --git a/test/core/end2end/dualstack_socket_test.c b/test/core/end2end/dualstack_socket_test.c
index d56df629d85a12df381260477e728d417ca8cb0f..06cbba295e6d73d6a6dae12b9b5f518061e67115 100644
--- a/test/core/end2end/dualstack_socket_test.c
+++ b/test/core/end2end/dualstack_socket_test.c
@@ -213,7 +213,8 @@ void test_connect(const char *server_host, const char *client_host, int port,
   grpc_completion_queue_destroy(client_cq);
 
   /* Destroy server. */
-  grpc_server_shutdown(server);
+  grpc_server_shutdown_and_notify(server, server_cq, tag(1000));
+  GPR_ASSERT(grpc_completion_queue_pluck(server_cq, tag(1000), GRPC_TIMEOUT_SECONDS_TO_DEADLINE(5)).type == GRPC_OP_COMPLETE);
   grpc_server_destroy(server);
   grpc_completion_queue_shutdown(server_cq);
   drain_cq(server_cq);
diff --git a/test/core/end2end/tests/bad_hostname.c b/test/core/end2end/tests/bad_hostname.c
index 396c1f20f804b191f25ee3e6d883321de4ece2f8..809c5bb7d4dab8467a4157fc1e8c978329d22dde 100644
--- a/test/core/end2end/tests/bad_hostname.c
+++ b/test/core/end2end/tests/bad_hostname.c
@@ -76,7 +76,8 @@ static void drain_cq(grpc_completion_queue *cq) {
 
 static void shutdown_server(grpc_end2end_test_fixture *f) {
   if (!f->server) return;
-  grpc_server_shutdown(f->server);
+  grpc_server_shutdown_and_notify(f->server, f->server_cq, tag(1000));
+  GPR_ASSERT(grpc_completion_queue_pluck(f->server_cq, tag(1000), GRPC_TIMEOUT_SECONDS_TO_DEADLINE(5)).type == GRPC_OP_COMPLETE);
   grpc_server_destroy(f->server);
   f->server = NULL;
 }
diff --git a/test/core/end2end/tests/cancel_after_accept.c b/test/core/end2end/tests/cancel_after_accept.c
index aa1978c989252cf510d4c1f402174185ae3521ff..cc5586bd1bef2880fe44aed91535b7ec80cd6003 100644
--- a/test/core/end2end/tests/cancel_after_accept.c
+++ b/test/core/end2end/tests/cancel_after_accept.c
@@ -75,7 +75,8 @@ static void drain_cq(grpc_completion_queue *cq) {
 
 static void shutdown_server(grpc_end2end_test_fixture *f) {
   if (!f->server) return;
-  grpc_server_shutdown(f->server);
+  grpc_server_shutdown_and_notify(f->server, f->server_cq, tag(1000));
+  GPR_ASSERT(grpc_completion_queue_pluck(f->server_cq, tag(1000), GRPC_TIMEOUT_SECONDS_TO_DEADLINE(5)).type == GRPC_OP_COMPLETE);
   grpc_server_destroy(f->server);
   f->server = NULL;
 }
diff --git a/test/core/end2end/tests/cancel_after_accept_and_writes_closed.c b/test/core/end2end/tests/cancel_after_accept_and_writes_closed.c
index a38d74bfa8f9e8bcd096beff6d67124d72ab9f27..9a2fcf68a46b837258f4c48110570bc9326ccb22 100644
--- a/test/core/end2end/tests/cancel_after_accept_and_writes_closed.c
+++ b/test/core/end2end/tests/cancel_after_accept_and_writes_closed.c
@@ -75,7 +75,8 @@ static void drain_cq(grpc_completion_queue *cq) {
 
 static void shutdown_server(grpc_end2end_test_fixture *f) {
   if (!f->server) return;
-  grpc_server_shutdown(f->server);
+  grpc_server_shutdown_and_notify(f->server, f->server_cq, tag(1000));
+  GPR_ASSERT(grpc_completion_queue_pluck(f->server_cq, tag(1000), GRPC_TIMEOUT_SECONDS_TO_DEADLINE(5)).type == GRPC_OP_COMPLETE);
   grpc_server_destroy(f->server);
   f->server = NULL;
 }
diff --git a/test/core/end2end/tests/cancel_after_invoke.c b/test/core/end2end/tests/cancel_after_invoke.c
index a0830f0e672d2fcf100079762381425a639c4712..9b3ad280cb66fea6c2c2f007ce88ad9daac39618 100644
--- a/test/core/end2end/tests/cancel_after_invoke.c
+++ b/test/core/end2end/tests/cancel_after_invoke.c
@@ -76,7 +76,8 @@ static void drain_cq(grpc_completion_queue *cq) {
 
 static void shutdown_server(grpc_end2end_test_fixture *f) {
   if (!f->server) return;
-  grpc_server_shutdown(f->server);
+  grpc_server_shutdown_and_notify(f->server, f->server_cq, tag(1000));
+  GPR_ASSERT(grpc_completion_queue_pluck(f->server_cq, tag(1000), GRPC_TIMEOUT_SECONDS_TO_DEADLINE(5)).type == GRPC_OP_COMPLETE);
   grpc_server_destroy(f->server);
   f->server = NULL;
 }
diff --git a/test/core/end2end/tests/cancel_before_invoke.c b/test/core/end2end/tests/cancel_before_invoke.c
index 142e8d43c8106caa65d6280eb178989e4968c0b6..ca19c469f61f39d2b0234669e71ac2e9297c9534 100644
--- a/test/core/end2end/tests/cancel_before_invoke.c
+++ b/test/core/end2end/tests/cancel_before_invoke.c
@@ -74,7 +74,8 @@ static void drain_cq(grpc_completion_queue *cq) {
 
 static void shutdown_server(grpc_end2end_test_fixture *f) {
   if (!f->server) return;
-  grpc_server_shutdown(f->server);
+  grpc_server_shutdown_and_notify(f->server, f->server_cq, tag(1000));
+  GPR_ASSERT(grpc_completion_queue_pluck(f->server_cq, tag(1000), GRPC_TIMEOUT_SECONDS_TO_DEADLINE(5)).type == GRPC_OP_COMPLETE);
   grpc_server_destroy(f->server);
   f->server = NULL;
 }
diff --git a/test/core/end2end/tests/cancel_in_a_vacuum.c b/test/core/end2end/tests/cancel_in_a_vacuum.c
index f0984cb5dc1d5494c8c8fdbb9eebc24d1d97c45b..5d7d762e5fb8363dd3e0bc67d5ffe10ee22bb78f 100644
--- a/test/core/end2end/tests/cancel_in_a_vacuum.c
+++ b/test/core/end2end/tests/cancel_in_a_vacuum.c
@@ -46,6 +46,8 @@
 
 enum { TIMEOUT = 200000 };
 
+static void *tag(gpr_intptr t) { return (void *)t; }
+
 static grpc_end2end_test_fixture begin_test(grpc_end2end_test_config config,
                                             const char *test_name,
                                             grpc_channel_args *client_args,
@@ -73,7 +75,8 @@ static void drain_cq(grpc_completion_queue *cq) {
 
 static void shutdown_server(grpc_end2end_test_fixture *f) {
   if (!f->server) return;
-  grpc_server_shutdown(f->server);
+  grpc_server_shutdown_and_notify(f->server, f->server_cq, tag(1000));
+  GPR_ASSERT(grpc_completion_queue_pluck(f->server_cq, tag(1000), GRPC_TIMEOUT_SECONDS_TO_DEADLINE(5)).type == GRPC_OP_COMPLETE);
   grpc_server_destroy(f->server);
   f->server = NULL;
 }
diff --git a/test/core/end2end/tests/census_simple_request.c b/test/core/end2end/tests/census_simple_request.c
index 347a6adcc16c238d0f149981286109be0f432626..ac2574c8040ce12da9102adf3f525f83ebccab3a 100644
--- a/test/core/end2end/tests/census_simple_request.c
+++ b/test/core/end2end/tests/census_simple_request.c
@@ -61,9 +61,12 @@ static grpc_end2end_test_fixture begin_test(grpc_end2end_test_config config,
   return f;
 }
 
+static void *tag(gpr_intptr t) { return (void *)t; }
+
 static void shutdown_server(grpc_end2end_test_fixture *f) {
   if (!f->server) return;
-  grpc_server_shutdown(f->server);
+  grpc_server_shutdown_and_notify(f->server, f->server_cq, tag(1000));
+  GPR_ASSERT(grpc_completion_queue_pluck(f->server_cq, tag(1000), GRPC_TIMEOUT_SECONDS_TO_DEADLINE(5)).type == GRPC_OP_COMPLETE);
   grpc_server_destroy(f->server);
   f->server = NULL;
 }
@@ -93,8 +96,6 @@ static void end_test(grpc_end2end_test_fixture *f) {
   grpc_completion_queue_destroy(f->client_cq);
 }
 
-static void *tag(gpr_intptr t) { return (void *)t; }
-
 static void test_body(grpc_end2end_test_fixture f) {
   grpc_call *c;
   grpc_call *s;
diff --git a/test/core/end2end/tests/disappearing_server.c b/test/core/end2end/tests/disappearing_server.c
index 62e58d8b0d488279f8ce0d8b2b4f8b9fe4ee1de3..cf9b79f117a6435754c37219efbd155403627498 100644
--- a/test/core/end2end/tests/disappearing_server.c
+++ b/test/core/end2end/tests/disappearing_server.c
@@ -62,7 +62,6 @@ static void drain_cq(grpc_completion_queue *cq) {
 
 static void shutdown_server(grpc_end2end_test_fixture *f) {
   if (!f->server) return;
-  grpc_server_shutdown(f->server);
   grpc_server_destroy(f->server);
   f->server = NULL;
 }
@@ -141,7 +140,7 @@ static void do_request_and_shutdown_server(grpc_end2end_test_fixture *f,
 
   /* should be able to shut down the server early
      - and still complete the request */
-  grpc_server_shutdown(f->server);
+  grpc_server_shutdown_and_notify(f->server, f->server_cq, tag(1000));
 
   op = ops;
   op->op = GRPC_OP_SEND_INITIAL_METADATA;
@@ -161,6 +160,7 @@ static void do_request_and_shutdown_server(grpc_end2end_test_fixture *f,
   GPR_ASSERT(GRPC_CALL_OK == grpc_call_start_batch(s, ops, op - ops, tag(102)));
 
   cq_expect_completion(v_server, tag(102), 1);
+  cq_expect_completion(v_server, tag(1000), 1);
   cq_verify(v_server);
 
   cq_expect_completion(v_client, tag(1), 1);
diff --git a/test/core/end2end/tests/early_server_shutdown_finishes_inflight_calls.c b/test/core/end2end/tests/early_server_shutdown_finishes_inflight_calls.c
index a924ae58bb19e7461a73c04af3973022adc8e9c5..5826ababaa282c286ab7fa0eb8936c9c5233c39a 100644
--- a/test/core/end2end/tests/early_server_shutdown_finishes_inflight_calls.c
+++ b/test/core/end2end/tests/early_server_shutdown_finishes_inflight_calls.c
@@ -72,13 +72,6 @@ static void drain_cq(grpc_completion_queue *cq) {
   } while (ev.type != GRPC_QUEUE_SHUTDOWN);
 }
 
-static void shutdown_server(grpc_end2end_test_fixture *f) {
-  if (!f->server) return;
-  grpc_server_shutdown(f->server);
-  grpc_server_destroy(f->server);
-  f->server = NULL;
-}
-
 static void shutdown_client(grpc_end2end_test_fixture *f) {
   if (!f->client) return;
   grpc_channel_destroy(f->client);
@@ -86,7 +79,6 @@ static void shutdown_client(grpc_end2end_test_fixture *f) {
 }
 
 static void end_test(grpc_end2end_test_fixture *f) {
-  shutdown_server(f);
   shutdown_client(f);
 
   grpc_completion_queue_shutdown(f->server_cq);
@@ -162,11 +154,15 @@ static void test_early_server_shutdown_finishes_inflight_calls(
   GPR_ASSERT(GRPC_CALL_OK == grpc_call_start_batch(s, ops, op - ops, tag(102)));
 
   /* shutdown and destroy the server */
-  shutdown_server(&f);
+  grpc_server_shutdown_and_notify(f.server, f.server_cq, tag(1000));
+  grpc_server_cancel_all_calls(f.server);
 
   cq_expect_completion(v_server, tag(102), 1);
+  cq_expect_completion(v_server, tag(1000), 1);
   cq_verify(v_server);
 
+  grpc_server_destroy(f.server);
+
   cq_expect_completion(v_client, tag(1), 1);
   cq_verify(v_client);
 
diff --git a/test/core/end2end/tests/early_server_shutdown_finishes_tags.c b/test/core/end2end/tests/early_server_shutdown_finishes_tags.c
index a8eb2144bbb0e5877e24bf868e4eac70513f5e92..0c2cdb78f0cf1d95432280dec0cd0dd9285944b8 100644
--- a/test/core/end2end/tests/early_server_shutdown_finishes_tags.c
+++ b/test/core/end2end/tests/early_server_shutdown_finishes_tags.c
@@ -72,13 +72,6 @@ static void drain_cq(grpc_completion_queue *cq) {
   } while (ev.type != GRPC_QUEUE_SHUTDOWN);
 }
 
-static void shutdown_server(grpc_end2end_test_fixture *f) {
-  if (!f->server) return;
-  /* don't shutdown, just destroy, to tickle this code edge */
-  grpc_server_destroy(f->server);
-  f->server = NULL;
-}
-
 static void shutdown_client(grpc_end2end_test_fixture *f) {
   if (!f->client) return;
   grpc_channel_destroy(f->client);
@@ -86,7 +79,6 @@ static void shutdown_client(grpc_end2end_test_fixture *f) {
 }
 
 static void end_test(grpc_end2end_test_fixture *f) {
-  shutdown_server(f);
   shutdown_client(f);
 
   grpc_completion_queue_shutdown(f->server_cq);
@@ -114,11 +106,14 @@ static void test_early_server_shutdown_finishes_tags(
              grpc_server_request_call(f.server, &s, &call_details,
                                       &request_metadata_recv, f.server_cq,
                                       f.server_cq, tag(101)));
-  grpc_server_shutdown(f.server);
+  grpc_server_shutdown_and_notify(f.server, f.server_cq, tag(1000));
   cq_expect_completion(v_server, tag(101), 0);
+  cq_expect_completion(v_server, tag(1000), 1);
   cq_verify(v_server);
   GPR_ASSERT(s == NULL);
 
+  grpc_server_destroy(f.server);
+
   end_test(&f);
   config.tear_down_data(&f);
   cq_verifier_destroy(v_server);
diff --git a/test/core/end2end/tests/empty_batch.c b/test/core/end2end/tests/empty_batch.c
index d1e5527e9e8fcdf89dbdf2d41738e9f05bf2b999..79d19659812672de6c7be375b5dbfdf163350267 100644
--- a/test/core/end2end/tests/empty_batch.c
+++ b/test/core/end2end/tests/empty_batch.c
@@ -76,7 +76,8 @@ static void drain_cq(grpc_completion_queue *cq) {
 
 static void shutdown_server(grpc_end2end_test_fixture *f) {
   if (!f->server) return;
-  grpc_server_shutdown(f->server);
+  grpc_server_shutdown_and_notify(f->server, f->server_cq, tag(1000));
+  GPR_ASSERT(grpc_completion_queue_pluck(f->server_cq, tag(1000), GRPC_TIMEOUT_SECONDS_TO_DEADLINE(5)).type == GRPC_OP_COMPLETE);
   grpc_server_destroy(f->server);
   f->server = NULL;
 }
diff --git a/test/core/end2end/tests/graceful_server_shutdown.c b/test/core/end2end/tests/graceful_server_shutdown.c
index 1a40966d7457c4d1570b6f8f7d33550a333c8b63..6a5b4b15d3d53924ee87ffef49912f58a2dd68f4 100644
--- a/test/core/end2end/tests/graceful_server_shutdown.c
+++ b/test/core/end2end/tests/graceful_server_shutdown.c
@@ -154,7 +154,7 @@ static void test_early_server_shutdown_finishes_inflight_calls(
   cq_verify(v_server);
 
   /* shutdown and destroy the server */
-  grpc_server_shutdown_and_notify(f.server, tag(0xdead));
+  grpc_server_shutdown_and_notify(f.server, f.server_cq, tag(0xdead));
   cq_verify_empty(v_server);
 
   op = ops;
@@ -175,11 +175,10 @@ static void test_early_server_shutdown_finishes_inflight_calls(
   GPR_ASSERT(GRPC_CALL_OK == grpc_call_start_batch(s, ops, op - ops, tag(102)));
 
   cq_expect_completion(v_server, tag(102), 1);
+  cq_expect_completion(v_server, tag(0xdead), 1);
   cq_verify(v_server);
 
   grpc_call_destroy(s);
-  cq_expect_completion(v_server, tag(0xdead), 1);
-  cq_verify(v_server);
 
   cq_expect_completion(v_client, tag(1), 1);
   cq_verify(v_client);
diff --git a/test/core/end2end/tests/invoke_large_request.c b/test/core/end2end/tests/invoke_large_request.c
index a1750ed7447990fa11daf8ed33ba60b616bb0543..812bdfa3e73cf938966118ba2e1c7ee41ccb99a0 100644
--- a/test/core/end2end/tests/invoke_large_request.c
+++ b/test/core/end2end/tests/invoke_large_request.c
@@ -72,7 +72,8 @@ static void drain_cq(grpc_completion_queue *cq) {
 
 static void shutdown_server(grpc_end2end_test_fixture *f) {
   if (!f->server) return;
-  grpc_server_shutdown(f->server);
+  grpc_server_shutdown_and_notify(f->server, f->server_cq, tag(1000));
+  GPR_ASSERT(grpc_completion_queue_pluck(f->server_cq, tag(1000), GRPC_TIMEOUT_SECONDS_TO_DEADLINE(5)).type == GRPC_OP_COMPLETE);
   grpc_server_destroy(f->server);
   f->server = NULL;
 }
diff --git a/test/core/end2end/tests/max_concurrent_streams.c b/test/core/end2end/tests/max_concurrent_streams.c
index 1e29b6668bb51972bc414f197f4cdbe3c7cd037d..f8660bb9013ca3959e1708c1858dd61b1fb24286 100644
--- a/test/core/end2end/tests/max_concurrent_streams.c
+++ b/test/core/end2end/tests/max_concurrent_streams.c
@@ -74,7 +74,8 @@ static void drain_cq(grpc_completion_queue *cq) {
 
 static void shutdown_server(grpc_end2end_test_fixture *f) {
   if (!f->server) return;
-  grpc_server_shutdown(f->server);
+  grpc_server_shutdown_and_notify(f->server, f->server_cq, tag(1000));
+  GPR_ASSERT(grpc_completion_queue_pluck(f->server_cq, tag(1000), GRPC_TIMEOUT_SECONDS_TO_DEADLINE(5)).type == GRPC_OP_COMPLETE);
   grpc_server_destroy(f->server);
   f->server = NULL;
 }
diff --git a/test/core/end2end/tests/max_message_length.c b/test/core/end2end/tests/max_message_length.c
index 119c925f6cedb85c2e1b4004a078992dae3dc569..8d3d2f10d9613f0b045a029513773d84891f782a 100644
--- a/test/core/end2end/tests/max_message_length.c
+++ b/test/core/end2end/tests/max_message_length.c
@@ -74,7 +74,8 @@ static void drain_cq(grpc_completion_queue *cq) {
 
 static void shutdown_server(grpc_end2end_test_fixture *f) {
   if (!f->server) return;
-  grpc_server_shutdown(f->server);
+  grpc_server_shutdown_and_notify(f->server, f->server_cq, tag(1000));
+  GPR_ASSERT(grpc_completion_queue_pluck(f->server_cq, tag(1000), GRPC_TIMEOUT_SECONDS_TO_DEADLINE(5)).type == GRPC_OP_COMPLETE);
   grpc_server_destroy(f->server);
   f->server = NULL;
 }
diff --git a/test/core/end2end/tests/no_op.c b/test/core/end2end/tests/no_op.c
index 5b18efcbfdfb02d0bb3b9af3c8e0d6dc06db0c3d..1f32691e63393dd9ca0fb5c9bb8aa28ff45ceb45 100644
--- a/test/core/end2end/tests/no_op.c
+++ b/test/core/end2end/tests/no_op.c
@@ -45,6 +45,8 @@
 
 enum { TIMEOUT = 200000 };
 
+static void *tag(gpr_intptr t) { return (void *)t; }
+
 static grpc_end2end_test_fixture begin_test(grpc_end2end_test_config config,
                                             const char *test_name,
                                             grpc_channel_args *client_args,
@@ -72,7 +74,8 @@ static void drain_cq(grpc_completion_queue *cq) {
 
 static void shutdown_server(grpc_end2end_test_fixture *f) {
   if (!f->server) return;
-  grpc_server_shutdown(f->server);
+  grpc_server_shutdown_and_notify(f->server, f->server_cq, tag(1000));
+  GPR_ASSERT(grpc_completion_queue_pluck(f->server_cq, tag(1000), GRPC_TIMEOUT_SECONDS_TO_DEADLINE(5)).type == GRPC_OP_COMPLETE);
   grpc_server_destroy(f->server);
   f->server = NULL;
 }
diff --git a/test/core/end2end/tests/ping_pong_streaming.c b/test/core/end2end/tests/ping_pong_streaming.c
index e4042b3a950d40ee57210bfd6e24afa579bdd442..2b617ce00b4735e5d13bd8bce63b343d24c7427a 100644
--- a/test/core/end2end/tests/ping_pong_streaming.c
+++ b/test/core/end2end/tests/ping_pong_streaming.c
@@ -74,7 +74,8 @@ static void drain_cq(grpc_completion_queue *cq) {
 
 static void shutdown_server(grpc_end2end_test_fixture *f) {
   if (!f->server) return;
-  grpc_server_shutdown(f->server);
+  grpc_server_shutdown_and_notify(f->server, f->server_cq, tag(1000));
+  GPR_ASSERT(grpc_completion_queue_pluck(f->server_cq, tag(1000), GRPC_TIMEOUT_SECONDS_TO_DEADLINE(5)).type == GRPC_OP_COMPLETE);
   grpc_server_destroy(f->server);
   f->server = NULL;
 }
diff --git a/test/core/end2end/tests/registered_call.c b/test/core/end2end/tests/registered_call.c
index 44f2533057b64ab28fd6c3f9f01bcb0e5d269347..43e34ff67369141d43491825e08fe6d7b1953146 100644
--- a/test/core/end2end/tests/registered_call.c
+++ b/test/core/end2end/tests/registered_call.c
@@ -76,7 +76,8 @@ static void drain_cq(grpc_completion_queue *cq) {
 
 static void shutdown_server(grpc_end2end_test_fixture *f) {
   if (!f->server) return;
-  grpc_server_shutdown(f->server);
+  grpc_server_shutdown_and_notify(f->server, f->server_cq, tag(1000));
+  GPR_ASSERT(grpc_completion_queue_pluck(f->server_cq, tag(1000), GRPC_TIMEOUT_SECONDS_TO_DEADLINE(5)).type == GRPC_OP_COMPLETE);
   grpc_server_destroy(f->server);
   f->server = NULL;
 }
diff --git a/test/core/end2end/tests/request_response_with_binary_metadata_and_payload.c b/test/core/end2end/tests/request_response_with_binary_metadata_and_payload.c
index d7bfe71fd7c606c889b6a071d1d65debd470c341..b1c6c98b0f6cef81ba4e93fe1ca519880a54b812 100644
--- a/test/core/end2end/tests/request_response_with_binary_metadata_and_payload.c
+++ b/test/core/end2end/tests/request_response_with_binary_metadata_and_payload.c
@@ -74,7 +74,8 @@ static void drain_cq(grpc_completion_queue *cq) {
 
 static void shutdown_server(grpc_end2end_test_fixture *f) {
   if (!f->server) return;
-  grpc_server_shutdown(f->server);
+  grpc_server_shutdown_and_notify(f->server, f->server_cq, tag(1000));
+  GPR_ASSERT(grpc_completion_queue_pluck(f->server_cq, tag(1000), GRPC_TIMEOUT_SECONDS_TO_DEADLINE(5)).type == GRPC_OP_COMPLETE);
   grpc_server_destroy(f->server);
   f->server = NULL;
 }
diff --git a/test/core/end2end/tests/request_response_with_metadata_and_payload.c b/test/core/end2end/tests/request_response_with_metadata_and_payload.c
index b8236075e90330cf70505ff131837bfaf61105ac..d0a7b9a4b5fb793118d9e5b5584dc027d6290e4e 100644
--- a/test/core/end2end/tests/request_response_with_metadata_and_payload.c
+++ b/test/core/end2end/tests/request_response_with_metadata_and_payload.c
@@ -74,7 +74,8 @@ static void drain_cq(grpc_completion_queue *cq) {
 
 static void shutdown_server(grpc_end2end_test_fixture *f) {
   if (!f->server) return;
-  grpc_server_shutdown(f->server);
+  grpc_server_shutdown_and_notify(f->server, f->server_cq, tag(1000));
+  GPR_ASSERT(grpc_completion_queue_pluck(f->server_cq, tag(1000), GRPC_TIMEOUT_SECONDS_TO_DEADLINE(5)).type == GRPC_OP_COMPLETE);
   grpc_server_destroy(f->server);
   f->server = NULL;
 }
diff --git a/test/core/end2end/tests/request_response_with_payload.c b/test/core/end2end/tests/request_response_with_payload.c
index da32215867b0729775e2a5a2dcb913cf49bc824a..9bf4816b4ec1f121e65b32a1c1f82b64b7d6cda4 100644
--- a/test/core/end2end/tests/request_response_with_payload.c
+++ b/test/core/end2end/tests/request_response_with_payload.c
@@ -74,7 +74,8 @@ static void drain_cq(grpc_completion_queue *cq) {
 
 static void shutdown_server(grpc_end2end_test_fixture *f) {
   if (!f->server) return;
-  grpc_server_shutdown(f->server);
+  grpc_server_shutdown_and_notify(f->server, f->server_cq, tag(1000));
+  GPR_ASSERT(grpc_completion_queue_pluck(f->server_cq, tag(1000), GRPC_TIMEOUT_SECONDS_TO_DEADLINE(5)).type == GRPC_OP_COMPLETE);
   grpc_server_destroy(f->server);
   f->server = NULL;
 }
diff --git a/test/core/end2end/tests/request_response_with_payload_and_call_creds.c b/test/core/end2end/tests/request_response_with_payload_and_call_creds.c
index 387c784064850dc8c1fc4c96fcfb6dc858e463bd..3de1d86757498ffb8785a066a8c709ddf483fb62 100644
--- a/test/core/end2end/tests/request_response_with_payload_and_call_creds.c
+++ b/test/core/end2end/tests/request_response_with_payload_and_call_creds.c
@@ -88,7 +88,8 @@ static void drain_cq(grpc_completion_queue *cq) {
 
 static void shutdown_server(grpc_end2end_test_fixture *f) {
   if (!f->server) return;
-  grpc_server_shutdown(f->server);
+  grpc_server_shutdown_and_notify(f->server, f->server_cq, tag(1000));
+  GPR_ASSERT(grpc_completion_queue_pluck(f->server_cq, tag(1000), GRPC_TIMEOUT_SECONDS_TO_DEADLINE(5)).type == GRPC_OP_COMPLETE);
   grpc_server_destroy(f->server);
   f->server = NULL;
 }
diff --git a/test/core/end2end/tests/request_response_with_trailing_metadata_and_payload.c b/test/core/end2end/tests/request_response_with_trailing_metadata_and_payload.c
index bd7276186ad570ded8f7a10d6d1918779f5764e7..e91ebf41c626367adf008cd6708f985e9c728069 100644
--- a/test/core/end2end/tests/request_response_with_trailing_metadata_and_payload.c
+++ b/test/core/end2end/tests/request_response_with_trailing_metadata_and_payload.c
@@ -74,7 +74,8 @@ static void drain_cq(grpc_completion_queue *cq) {
 
 static void shutdown_server(grpc_end2end_test_fixture *f) {
   if (!f->server) return;
-  grpc_server_shutdown(f->server);
+  grpc_server_shutdown_and_notify(f->server, f->server_cq, tag(1000));
+  GPR_ASSERT(grpc_completion_queue_pluck(f->server_cq, tag(1000), GRPC_TIMEOUT_SECONDS_TO_DEADLINE(5)).type == GRPC_OP_COMPLETE);
   grpc_server_destroy(f->server);
   f->server = NULL;
 }
diff --git a/test/core/end2end/tests/request_with_flags.c b/test/core/end2end/tests/request_with_flags.c
index f9d6081577aaaeb5625e80f73ee267a981e8ab09..5ad475544cd1e6b5000818bd2274f4218f34de00 100644
--- a/test/core/end2end/tests/request_with_flags.c
+++ b/test/core/end2end/tests/request_with_flags.c
@@ -75,7 +75,8 @@ static void drain_cq(grpc_completion_queue *cq) {
 
 static void shutdown_server(grpc_end2end_test_fixture *f) {
   if (!f->server) return;
-  grpc_server_shutdown(f->server);
+  grpc_server_shutdown_and_notify(f->server, f->server_cq, tag(1000));
+  GPR_ASSERT(grpc_completion_queue_pluck(f->server_cq, tag(1000), GRPC_TIMEOUT_SECONDS_TO_DEADLINE(5)).type == GRPC_OP_COMPLETE);
   grpc_server_destroy(f->server);
   f->server = NULL;
 }
diff --git a/test/core/end2end/tests/request_with_large_metadata.c b/test/core/end2end/tests/request_with_large_metadata.c
index 56acbed10bfe1161215c9f476dde6fefccd94b3f..cf6f4b23e4ef7b8919fc1e43b65a246dcd2a2b99 100644
--- a/test/core/end2end/tests/request_with_large_metadata.c
+++ b/test/core/end2end/tests/request_with_large_metadata.c
@@ -74,7 +74,8 @@ static void drain_cq(grpc_completion_queue *cq) {
 
 static void shutdown_server(grpc_end2end_test_fixture *f) {
   if (!f->server) return;
-  grpc_server_shutdown(f->server);
+  grpc_server_shutdown_and_notify(f->server, f->server_cq, tag(1000));
+  GPR_ASSERT(grpc_completion_queue_pluck(f->server_cq, tag(1000), GRPC_TIMEOUT_SECONDS_TO_DEADLINE(5)).type == GRPC_OP_COMPLETE);
   grpc_server_destroy(f->server);
   f->server = NULL;
 }
diff --git a/test/core/end2end/tests/request_with_payload.c b/test/core/end2end/tests/request_with_payload.c
index bf5aa1421d7e479499a9bbfaae744f54825df08f..935aa5878b87c855fa13d8afc45c513b203788df 100644
--- a/test/core/end2end/tests/request_with_payload.c
+++ b/test/core/end2end/tests/request_with_payload.c
@@ -74,7 +74,8 @@ static void drain_cq(grpc_completion_queue *cq) {
 
 static void shutdown_server(grpc_end2end_test_fixture *f) {
   if (!f->server) return;
-  grpc_server_shutdown(f->server);
+  grpc_server_shutdown_and_notify(f->server, f->server_cq, tag(1000));
+  GPR_ASSERT(grpc_completion_queue_pluck(f->server_cq, tag(1000), GRPC_TIMEOUT_SECONDS_TO_DEADLINE(5)).type == GRPC_OP_COMPLETE);
   grpc_server_destroy(f->server);
   f->server = NULL;
 }
diff --git a/test/core/end2end/tests/server_finishes_request.c b/test/core/end2end/tests/server_finishes_request.c
index a10708ebe2ed8089384ed2fc1e6a030aef05bdcc..973f024b4d0d495b0df53c8f59a8bce2d57986fa 100644
--- a/test/core/end2end/tests/server_finishes_request.c
+++ b/test/core/end2end/tests/server_finishes_request.c
@@ -76,7 +76,8 @@ static void drain_cq(grpc_completion_queue *cq) {
 
 static void shutdown_server(grpc_end2end_test_fixture *f) {
   if (!f->server) return;
-  grpc_server_shutdown(f->server);
+  grpc_server_shutdown_and_notify(f->server, f->server_cq, tag(1000));
+  GPR_ASSERT(grpc_completion_queue_pluck(f->server_cq, tag(1000), GRPC_TIMEOUT_SECONDS_TO_DEADLINE(5)).type == GRPC_OP_COMPLETE);
   grpc_server_destroy(f->server);
   f->server = NULL;
 }
diff --git a/test/core/end2end/tests/simple_delayed_request.c b/test/core/end2end/tests/simple_delayed_request.c
index 0bd4e2a1a7d5bc8612479c61463ee76a302dc366..36558eca1d6b3634a3d85bddfc49b24046075e05 100644
--- a/test/core/end2end/tests/simple_delayed_request.c
+++ b/test/core/end2end/tests/simple_delayed_request.c
@@ -62,7 +62,8 @@ static void drain_cq(grpc_completion_queue *cq) {
 
 static void shutdown_server(grpc_end2end_test_fixture *f) {
   if (!f->server) return;
-  grpc_server_shutdown(f->server);
+  grpc_server_shutdown_and_notify(f->server, f->server_cq, tag(1000));
+  GPR_ASSERT(grpc_completion_queue_pluck(f->server_cq, tag(1000), GRPC_TIMEOUT_SECONDS_TO_DEADLINE(5)).type == GRPC_OP_COMPLETE);
   grpc_server_destroy(f->server);
   f->server = NULL;
 }
diff --git a/test/core/end2end/tests/simple_request.c b/test/core/end2end/tests/simple_request.c
index a9acc8cd25c3c53c74d4099c4971b9558feaf18e..06f922351e3ae307d487c52b982aeeed3a8154c5 100644
--- a/test/core/end2end/tests/simple_request.c
+++ b/test/core/end2end/tests/simple_request.c
@@ -76,7 +76,8 @@ static void drain_cq(grpc_completion_queue *cq) {
 
 static void shutdown_server(grpc_end2end_test_fixture *f) {
   if (!f->server) return;
-  grpc_server_shutdown(f->server);
+  grpc_server_shutdown_and_notify(f->server, f->server_cq, tag(1000));
+  GPR_ASSERT(grpc_completion_queue_pluck(f->server_cq, tag(1000), GRPC_TIMEOUT_SECONDS_TO_DEADLINE(5)).type == GRPC_OP_COMPLETE);
   grpc_server_destroy(f->server);
   f->server = NULL;
 }
diff --git a/test/core/end2end/tests/simple_request_with_high_initial_sequence_number.c b/test/core/end2end/tests/simple_request_with_high_initial_sequence_number.c
index bc53481f8cd458924ad1a35fb87f60cdbbd974c0..1d7a150d7efe8e72d1222a8671df7837f61082d5 100644
--- a/test/core/end2end/tests/simple_request_with_high_initial_sequence_number.c
+++ b/test/core/end2end/tests/simple_request_with_high_initial_sequence_number.c
@@ -76,7 +76,8 @@ static void drain_cq(grpc_completion_queue *cq) {
 
 static void shutdown_server(grpc_end2end_test_fixture *f) {
   if (!f->server) return;
-  grpc_server_shutdown(f->server);
+  grpc_server_shutdown_and_notify(f->server, f->server_cq, tag(1000));
+  GPR_ASSERT(grpc_completion_queue_pluck(f->server_cq, tag(1000), GRPC_TIMEOUT_SECONDS_TO_DEADLINE(5)).type == GRPC_OP_COMPLETE);
   grpc_server_destroy(f->server);
   f->server = NULL;
 }
diff --git a/test/core/fling/server.c b/test/core/fling/server.c
index 48304ed8d7a1e7330a9df410271d67f9cb8e0562..e58d721c86c38f1ab99e2f71d306b6da61a4cfe5 100644
--- a/test/core/fling/server.c
+++ b/test/core/fling/server.c
@@ -233,7 +233,8 @@ int main(int argc, char **argv) {
   while (!shutdown_finished) {
     if (got_sigint && !shutdown_started) {
       gpr_log(GPR_INFO, "Shutting down due to SIGINT");
-      grpc_server_shutdown(server);
+      grpc_server_shutdown_and_notify(server, cq, tag(1000));
+      GPR_ASSERT(grpc_completion_queue_pluck(cq, tag(1000), GRPC_TIMEOUT_SECONDS_TO_DEADLINE(5)).type == GRPC_OP_COMPLETE);
       grpc_completion_queue_shutdown(cq);
       shutdown_started = 1;
     }
diff --git a/test/core/surface/byte_buffer_reader_test.c b/test/core/surface/byte_buffer_reader_test.c
index 87a2cd7a43b792a2108dc4e6e8ee6548f2634282..2eb354a80aa73cefc71e46f06433be46beaf071b 100644
--- a/test/core/surface/byte_buffer_reader_test.c
+++ b/test/core/surface/byte_buffer_reader_test.c
@@ -160,6 +160,30 @@ static void test_read_deflate_compressed_slice(void) {
   read_compressed_slice(GRPC_COMPRESS_DEFLATE, INPUT_SIZE);
 }
 
+static void test_byte_buffer_from_reader(void) {
+  gpr_slice slice;
+  grpc_byte_buffer *buffer, *buffer_from_reader;
+  grpc_byte_buffer_reader reader;
+
+  LOG_TEST("test_byte_buffer_from_reader");
+  slice = gpr_slice_malloc(4);
+  memcpy(GPR_SLICE_START_PTR(slice), "test", 4);
+  buffer = grpc_raw_byte_buffer_create(&slice, 1);
+  gpr_slice_unref(slice);
+  grpc_byte_buffer_reader_init(&reader, buffer);
+
+  buffer_from_reader = grpc_raw_byte_buffer_from_reader(&reader);
+  GPR_ASSERT(buffer->type == buffer_from_reader->type);
+  GPR_ASSERT(buffer_from_reader->data.raw.compression == GRPC_COMPRESS_NONE);
+  GPR_ASSERT(buffer_from_reader->data.raw.slice_buffer.count == 1);
+  GPR_ASSERT(memcmp(GPR_SLICE_START_PTR(
+                        buffer_from_reader->data.raw.slice_buffer.slices[0]),
+                    "test", 4) == 0);
+
+  grpc_byte_buffer_destroy(buffer);
+  grpc_byte_buffer_destroy(buffer_from_reader);
+}
+
 int main(int argc, char **argv) {
   grpc_test_init(argc, argv);
   test_read_one_slice();
@@ -167,6 +191,7 @@ int main(int argc, char **argv) {
   test_read_none_compressed_slice();
   test_read_gzip_compressed_slice();
   test_read_deflate_compressed_slice();
+  test_byte_buffer_from_reader();
 
   return 0;
 }
diff --git a/test/cpp/end2end/async_end2end_test.cc b/test/cpp/end2end/async_end2end_test.cc
index 24595a820f62582681299fe768977623863ee668..871f956491ceba69ec66b5c221701bd88f35888a 100644
--- a/test/cpp/end2end/async_end2end_test.cc
+++ b/test/cpp/end2end/async_end2end_test.cc
@@ -157,7 +157,7 @@ class AsyncEnd2endTest : public ::testing::Test {
       client_ok(4);
 
       EXPECT_EQ(send_response.message(), recv_response.message());
-      EXPECT_TRUE(recv_status.IsOk());
+      EXPECT_TRUE(recv_status.ok());
     }
   }
 
@@ -218,7 +218,7 @@ TEST_F(AsyncEnd2endTest, AsyncNextRpc) {
   verify_timed_ok(&cli_cq_, 4, true);
 
   EXPECT_EQ(send_response.message(), recv_response.message());
-  EXPECT_TRUE(recv_status.IsOk());
+  EXPECT_TRUE(recv_status.ok());
 }
 
 // Two pings and a final pong.
@@ -272,7 +272,7 @@ TEST_F(AsyncEnd2endTest, SimpleClientStreaming) {
   client_ok(10);
 
   EXPECT_EQ(send_response.message(), recv_response.message());
-  EXPECT_TRUE(recv_status.IsOk());
+  EXPECT_TRUE(recv_status.ok());
 }
 
 // One ping, two pongs.
@@ -323,7 +323,7 @@ TEST_F(AsyncEnd2endTest, SimpleServerStreaming) {
   cli_stream->Finish(&recv_status, tag(9));
   client_ok(9);
 
-  EXPECT_TRUE(recv_status.IsOk());
+  EXPECT_TRUE(recv_status.ok());
 }
 
 // One ping, one pong.
@@ -376,7 +376,7 @@ TEST_F(AsyncEnd2endTest, SimpleBidiStreaming) {
   cli_stream->Finish(&recv_status, tag(10));
   client_ok(10);
 
-  EXPECT_TRUE(recv_status.IsOk());
+  EXPECT_TRUE(recv_status.ok());
 }
 
 // Metadata tests
@@ -420,7 +420,7 @@ TEST_F(AsyncEnd2endTest, ClientInitialMetadataRpc) {
   client_ok(4);
 
   EXPECT_EQ(send_response.message(), recv_response.message());
-  EXPECT_TRUE(recv_status.IsOk());
+  EXPECT_TRUE(recv_status.ok());
 }
 
 TEST_F(AsyncEnd2endTest, ServerInitialMetadataRpc) {
@@ -467,7 +467,7 @@ TEST_F(AsyncEnd2endTest, ServerInitialMetadataRpc) {
   client_ok(6);
 
   EXPECT_EQ(send_response.message(), recv_response.message());
-  EXPECT_TRUE(recv_status.IsOk());
+  EXPECT_TRUE(recv_status.ok());
 }
 
 TEST_F(AsyncEnd2endTest, ServerTrailingMetadataRpc) {
@@ -507,7 +507,7 @@ TEST_F(AsyncEnd2endTest, ServerTrailingMetadataRpc) {
   response_reader->Finish(&recv_response, &recv_status, tag(5));
   client_ok(5);
   EXPECT_EQ(send_response.message(), recv_response.message());
-  EXPECT_TRUE(recv_status.IsOk());
+  EXPECT_TRUE(recv_status.ok());
   auto server_trailing_metadata = cli_ctx.GetServerTrailingMetadata();
   EXPECT_EQ(meta1.second, server_trailing_metadata.find(meta1.first)->second);
   EXPECT_EQ(meta2.second, server_trailing_metadata.find(meta2.first)->second);
@@ -580,7 +580,7 @@ TEST_F(AsyncEnd2endTest, MetadataRpc) {
   response_reader->Finish(&recv_response, &recv_status, tag(6));
   client_ok(6);
   EXPECT_EQ(send_response.message(), recv_response.message());
-  EXPECT_TRUE(recv_status.IsOk());
+  EXPECT_TRUE(recv_status.ok());
   auto server_trailing_metadata = cli_ctx.GetServerTrailingMetadata();
   EXPECT_EQ(meta5.second, server_trailing_metadata.find(meta5.first)->second);
   EXPECT_EQ(meta6.second, server_trailing_metadata.find(meta6.first)->second);
diff --git a/test/cpp/end2end/client_crash_test.cc b/test/cpp/end2end/client_crash_test.cc
index e86681f75a138c0b37683be6bca1a8beb6107f43..aeba29594770b88e0cebc6f4d404500f49f673f8 100644
--- a/test/cpp/end2end/client_crash_test.cc
+++ b/test/cpp/end2end/client_crash_test.cc
@@ -119,7 +119,7 @@ TEST_F(CrashTest, KillAfterWrite) {
 
   EXPECT_FALSE(stream->Read(&response));
 
-  EXPECT_FALSE(stream->Finish().IsOk());
+  EXPECT_FALSE(stream->Finish().ok());
 }
 
 TEST_F(CrashTest, KillBeforeWrite) {
@@ -142,7 +142,7 @@ TEST_F(CrashTest, KillBeforeWrite) {
   EXPECT_FALSE(stream->Write(request));
   EXPECT_FALSE(stream->Read(&response));
 
-  EXPECT_FALSE(stream->Finish().IsOk());
+  EXPECT_FALSE(stream->Finish().ok());
 }
 
 }  // namespace
diff --git a/test/cpp/end2end/end2end_test.cc b/test/cpp/end2end/end2end_test.cc
index f48cf953a4f6e60e7e8a38c241daef3beafcff96..45ba8b0878b4f64d1bee61a65aaa57dc31a44b59 100644
--- a/test/cpp/end2end/end2end_test.cc
+++ b/test/cpp/end2end/end2end_test.cc
@@ -101,13 +101,13 @@ class TestServiceImpl : public ::grpc::cpp::test::util::TestService::Service {
             gpr_now(),
             gpr_time_from_micros(request->param().client_cancel_after_us())));
       }
-      return Status::Cancelled;
+      return Status::CANCELLED;
     } else if (request->has_param() &&
                request->param().server_cancel_after_us()) {
       gpr_sleep_until(gpr_time_add(
             gpr_now(),
             gpr_time_from_micros(request->param().server_cancel_after_us())));
-      return Status::Cancelled;
+      return Status::CANCELLED;
     } else {
       EXPECT_FALSE(context->IsCancelled());
     }
@@ -232,7 +232,7 @@ static void SendRpc(grpc::cpp::test::util::TestService::Stub* stub,
     ClientContext context;
     Status s = stub->Echo(&context, request, &response);
     EXPECT_EQ(response.message(), request.message());
-    EXPECT_TRUE(s.IsOk());
+    EXPECT_TRUE(s.ok());
   }
 }
 
@@ -265,7 +265,7 @@ TEST_F(End2endTest, RpcDeadlineExpires) {
       std::chrono::system_clock::now() + std::chrono::microseconds(10);
   context.set_deadline(deadline);
   Status s = stub_->Echo(&context, request, &response);
-  EXPECT_EQ(StatusCode::DEADLINE_EXCEEDED, s.code());
+  EXPECT_EQ(StatusCode::DEADLINE_EXCEEDED, s.error_code());
 }
 
 // Set a long but finite deadline.
@@ -281,7 +281,7 @@ TEST_F(End2endTest, RpcLongDeadline) {
   context.set_deadline(deadline);
   Status s = stub_->Echo(&context, request, &response);
   EXPECT_EQ(response.message(), request.message());
-  EXPECT_TRUE(s.IsOk());
+  EXPECT_TRUE(s.ok());
 }
 
 // Ask server to echo back the deadline it sees.
@@ -298,7 +298,7 @@ TEST_F(End2endTest, EchoDeadline) {
   context.set_deadline(deadline);
   Status s = stub_->Echo(&context, request, &response);
   EXPECT_EQ(response.message(), request.message());
-  EXPECT_TRUE(s.IsOk());
+  EXPECT_TRUE(s.ok());
   gpr_timespec sent_deadline;
   Timepoint2Timespec(deadline, &sent_deadline);
   // Allow 1 second error.
@@ -317,7 +317,7 @@ TEST_F(End2endTest, EchoDeadlineForNoDeadlineRpc) {
   ClientContext context;
   Status s = stub_->Echo(&context, request, &response);
   EXPECT_EQ(response.message(), request.message());
-  EXPECT_TRUE(s.IsOk());
+  EXPECT_TRUE(s.ok());
   EXPECT_EQ(response.param().request_deadline(), gpr_inf_future.tv_sec);
 }
 
@@ -329,9 +329,9 @@ TEST_F(End2endTest, UnimplementedRpc) {
 
   ClientContext context;
   Status s = stub_->Unimplemented(&context, request, &response);
-  EXPECT_FALSE(s.IsOk());
-  EXPECT_EQ(s.code(), grpc::StatusCode::UNIMPLEMENTED);
-  EXPECT_EQ(s.details(), "");
+  EXPECT_FALSE(s.ok());
+  EXPECT_EQ(s.error_code(), grpc::StatusCode::UNIMPLEMENTED);
+  EXPECT_EQ(s.error_message(), "");
   EXPECT_EQ(response.message(), "");
 }
 
@@ -347,7 +347,7 @@ TEST_F(End2endTest, RequestStreamOneRequest) {
   stream->WritesDone();
   Status s = stream->Finish();
   EXPECT_EQ(response.message(), request.message());
-  EXPECT_TRUE(s.IsOk());
+  EXPECT_TRUE(s.ok());
 }
 
 TEST_F(End2endTest, RequestStreamTwoRequests) {
@@ -363,7 +363,7 @@ TEST_F(End2endTest, RequestStreamTwoRequests) {
   stream->WritesDone();
   Status s = stream->Finish();
   EXPECT_EQ(response.message(), "hellohello");
-  EXPECT_TRUE(s.IsOk());
+  EXPECT_TRUE(s.ok());
 }
 
 TEST_F(End2endTest, ResponseStream) {
@@ -383,7 +383,7 @@ TEST_F(End2endTest, ResponseStream) {
   EXPECT_FALSE(stream->Read(&response));
 
   Status s = stream->Finish();
-  EXPECT_TRUE(s.IsOk());
+  EXPECT_TRUE(s.ok());
 }
 
 TEST_F(End2endTest, BidiStream) {
@@ -414,7 +414,7 @@ TEST_F(End2endTest, BidiStream) {
   EXPECT_FALSE(stream->Read(&response));
 
   Status s = stream->Finish();
-  EXPECT_TRUE(s.IsOk());
+  EXPECT_TRUE(s.ok());
 }
 
 // Talk to the two services with the same name but different package names.
@@ -433,7 +433,7 @@ TEST_F(End2endTest, DiffPackageServices) {
   ClientContext context;
   Status s = stub->Echo(&context, request, &response);
   EXPECT_EQ(response.message(), request.message());
-  EXPECT_TRUE(s.IsOk());
+  EXPECT_TRUE(s.ok());
 
   std::unique_ptr<grpc::cpp::test::util::duplicate::TestService::Stub>
       dup_pkg_stub(
@@ -441,7 +441,7 @@ TEST_F(End2endTest, DiffPackageServices) {
   ClientContext context2;
   s = dup_pkg_stub->Echo(&context2, request, &response);
   EXPECT_EQ("no package", response.message());
-  EXPECT_TRUE(s.IsOk());
+  EXPECT_TRUE(s.ok());
 }
 
 // rpc and stream should fail on bad credentials.
@@ -459,16 +459,16 @@ TEST_F(End2endTest, BadCredentials) {
 
   Status s = stub->Echo(&context, request, &response);
   EXPECT_EQ("", response.message());
-  EXPECT_FALSE(s.IsOk());
-  EXPECT_EQ(StatusCode::UNKNOWN, s.code());
-  EXPECT_EQ("Rpc sent on a lame channel.", s.details());
+  EXPECT_FALSE(s.ok());
+  EXPECT_EQ(StatusCode::UNKNOWN, s.error_code());
+  EXPECT_EQ("Rpc sent on a lame channel.", s.error_message());
 
   ClientContext context2;
   auto stream = stub->BidiStream(&context2);
   s = stream->Finish();
-  EXPECT_FALSE(s.IsOk());
-  EXPECT_EQ(StatusCode::UNKNOWN, s.code());
-  EXPECT_EQ("Rpc sent on a lame channel.", s.details());
+  EXPECT_FALSE(s.ok());
+  EXPECT_EQ(StatusCode::UNKNOWN, s.error_code());
+  EXPECT_EQ("Rpc sent on a lame channel.", s.error_message());
 }
 
 void CancelRpc(ClientContext* context, int delay_us, TestServiceImpl* service) {
@@ -491,8 +491,8 @@ TEST_F(End2endTest, ClientCancelsRpc) {
   std::thread cancel_thread(CancelRpc, &context, kCancelDelayUs, &service_);
   Status s = stub_->Echo(&context, request, &response);
   cancel_thread.join();
-  EXPECT_EQ(StatusCode::CANCELLED, s.code());
-  EXPECT_EQ(s.details(), "Cancelled");
+  EXPECT_EQ(StatusCode::CANCELLED, s.error_code());
+  EXPECT_EQ(s.error_message(), "Cancelled");
 }
 
 // Server cancels rpc after 1ms
@@ -505,8 +505,8 @@ TEST_F(End2endTest, ServerCancelsRpc) {
 
   ClientContext context;
   Status s = stub_->Echo(&context, request, &response);
-  EXPECT_EQ(StatusCode::CANCELLED, s.code());
-  EXPECT_TRUE(s.details().empty());
+  EXPECT_EQ(StatusCode::CANCELLED, s.error_code());
+  EXPECT_TRUE(s.error_message().empty());
 }
 
 // Client cancels request stream after sending two messages
@@ -524,7 +524,7 @@ TEST_F(End2endTest, ClientCancelsRequestStream) {
   context.TryCancel();
 
   Status s = stream->Finish();
-  EXPECT_EQ(grpc::StatusCode::CANCELLED, s.code());
+  EXPECT_EQ(grpc::StatusCode::CANCELLED, s.error_code());
 
   EXPECT_EQ(response.message(), "");
 }
@@ -558,7 +558,7 @@ TEST_F(End2endTest, ClientCancelsResponseStream) {
   Status s = stream->Finish();
   // The final status could be either of CANCELLED or OK depending on
   // who won the race.
-  EXPECT_GE(grpc::StatusCode::CANCELLED, s.code());
+  EXPECT_GE(grpc::StatusCode::CANCELLED, s.error_code());
 }
 
 // Client cancels bidi stream after sending some messages
@@ -591,7 +591,7 @@ TEST_F(End2endTest, ClientCancelsBidi) {
   }
 
   Status s = stream->Finish();
-  EXPECT_EQ(grpc::StatusCode::CANCELLED, s.code());
+  EXPECT_EQ(grpc::StatusCode::CANCELLED, s.error_code());
 }
 
 TEST_F(End2endTest, RpcMaxMessageSize) {
@@ -602,7 +602,7 @@ TEST_F(End2endTest, RpcMaxMessageSize) {
 
   ClientContext context;
   Status s = stub_->Echo(&context, request, &response);
-  EXPECT_FALSE(s.IsOk());
+  EXPECT_FALSE(s.ok());
 }
 
 bool MetadataContains(const std::multimap<grpc::string, grpc::string>& metadata,
@@ -632,7 +632,7 @@ TEST_F(End2endTest, SetPerCallCredentials) {
 
   Status s = stub_->Echo(&context, request, &response);
   EXPECT_EQ(request.message(), response.message());
-  EXPECT_TRUE(s.IsOk());
+  EXPECT_TRUE(s.ok());
   EXPECT_TRUE(MetadataContains(context.GetServerTrailingMetadata(),
                                GRPC_IAM_AUTHORIZATION_TOKEN_METADATA_KEY,
                                "fake_token"));
@@ -652,8 +652,8 @@ TEST_F(End2endTest, InsecurePerCallCredentials) {
   request.mutable_param()->set_echo_metadata(true);
 
   Status s = stub_->Echo(&context, request, &response);
-  EXPECT_EQ(StatusCode::CANCELLED, s.code());
-  EXPECT_EQ("Failed to set credentials to rpc.", s.details());
+  EXPECT_EQ(StatusCode::CANCELLED, s.error_code());
+  EXPECT_EQ("Failed to set credentials to rpc.", s.error_message());
 }
 
 TEST_F(End2endTest, OverridePerCallCredentials) {
@@ -684,7 +684,7 @@ TEST_F(End2endTest, OverridePerCallCredentials) {
                                 GRPC_IAM_AUTHORITY_SELECTOR_METADATA_KEY,
                                 "fake_selector1"));
   EXPECT_EQ(request.message(), response.message());
-  EXPECT_TRUE(s.IsOk());
+  EXPECT_TRUE(s.ok());
 }
 
 }  // namespace testing
diff --git a/test/cpp/end2end/generic_end2end_test.cc b/test/cpp/end2end/generic_end2end_test.cc
index 80e43fd8544688ec1e74f1e3b78c18c3bea09dda..7132b6b1f180f88b01c127d8a91c6078c0683a4b 100644
--- a/test/cpp/end2end/generic_end2end_test.cc
+++ b/test/cpp/end2end/generic_end2end_test.cc
@@ -190,7 +190,7 @@ class GenericEnd2endTest : public ::testing::Test {
       client_ok(9);
 
       EXPECT_EQ(send_response.message(), recv_response.message());
-      EXPECT_TRUE(recv_status.IsOk());
+      EXPECT_TRUE(recv_status.ok());
     }
   }
 
@@ -273,7 +273,7 @@ TEST_F(GenericEnd2endTest, SimpleBidiStreaming) {
   client_ok(10);
 
   EXPECT_EQ(send_response.message(), recv_response.message());
-  EXPECT_TRUE(recv_status.IsOk());
+  EXPECT_TRUE(recv_status.ok());
 }
 
 }  // namespace
diff --git a/test/cpp/end2end/mock_test.cc b/test/cpp/end2end/mock_test.cc
index 0226da672c9fe7c7105706229a42fa29c985b20d..2809ab8d3cfb21909e9bfe95b30b60efa49577d0 100644
--- a/test/cpp/end2end/mock_test.cc
+++ b/test/cpp/end2end/mock_test.cc
@@ -168,7 +168,7 @@ class FakeClient {
     request.set_message("hello world");
     Status s = stub_->Echo(&context, request, &response);
     EXPECT_EQ(request.message(), response.message());
-    EXPECT_TRUE(s.IsOk());
+    EXPECT_TRUE(s.ok());
   }
 
   void DoBidiStream() {
@@ -199,7 +199,7 @@ class FakeClient {
     EXPECT_FALSE(stream->Read(&response));
 
     Status s = stream->Finish();
-    EXPECT_TRUE(s.IsOk());
+    EXPECT_TRUE(s.ok());
   }
 
   void ResetStub(TestService::StubInterface* stub) { stub_ = stub; }
diff --git a/test/cpp/end2end/thread_stress_test.cc b/test/cpp/end2end/thread_stress_test.cc
index 5ee29e40f43eb97a0e563e62e8fa57ff52f59faf..0b43dfd1062fd605e4973db10f531ecc9a006153 100644
--- a/test/cpp/end2end/thread_stress_test.cc
+++ b/test/cpp/end2end/thread_stress_test.cc
@@ -99,13 +99,13 @@ class TestServiceImpl : public ::grpc::cpp::test::util::TestService::Service {
             gpr_now(),
             gpr_time_from_micros(request->param().client_cancel_after_us())));
       }
-      return Status::Cancelled;
+      return Status::CANCELLED;
     } else if (request->has_param() &&
                request->param().server_cancel_after_us()) {
       gpr_sleep_until(gpr_time_add(
           gpr_now(),
           gpr_time_from_micros(request->param().server_cancel_after_us())));
-      return Status::Cancelled;
+      return Status::CANCELLED;
     } else {
       EXPECT_FALSE(context->IsCancelled());
     }
@@ -219,7 +219,7 @@ static void SendRpc(grpc::cpp::test::util::TestService::Stub* stub,
     ClientContext context;
     Status s = stub->Echo(&context, request, &response);
     EXPECT_EQ(response.message(), request.message());
-    EXPECT_TRUE(s.IsOk());
+    EXPECT_TRUE(s.ok());
   }
 }
 
diff --git a/test/cpp/interop/interop_client.cc b/test/cpp/interop/interop_client.cc
index e3510592468c1c3183b34f0a88bf424c33f02004..f08f90b13993098d15735682d198f6577bbd3960 100644
--- a/test/cpp/interop/interop_client.cc
+++ b/test/cpp/interop/interop_client.cc
@@ -65,11 +65,11 @@ InteropClient::InteropClient(std::shared_ptr<ChannelInterface> channel)
     : channel_(channel) {}
 
 void InteropClient::AssertOkOrPrintErrorStatus(const Status& s) {
-  if (s.IsOk()) {
+  if (s.ok()) {
     return;
   }
-  gpr_log(GPR_INFO, "Error status code: %d, message: %s", s.code(),
-          s.details().c_str());
+  gpr_log(GPR_INFO, "Error status code: %d, message: %s", s.error_code(),
+          s.error_message().c_str());
   GPR_ASSERT(0);
 }
 
@@ -321,7 +321,7 @@ void InteropClient::DoCancelAfterBegin() {
   gpr_log(GPR_INFO, "Trying to cancel...");
   context.TryCancel();
   Status s = stream->Finish();
-  GPR_ASSERT(s.code() == StatusCode::CANCELLED);
+  GPR_ASSERT(s.error_code() == StatusCode::CANCELLED);
   gpr_log(GPR_INFO, "Canceling streaming done.");
 }
 
diff --git a/test/cpp/qps/client_sync.cc b/test/cpp/qps/client_sync.cc
index d1682caf066742a0077f90d606ed4792b9dd44ce..718698bfe1dec0fba0cc482970a9409f215f04f1 100644
--- a/test/cpp/qps/client_sync.cc
+++ b/test/cpp/qps/client_sync.cc
@@ -103,7 +103,7 @@ class SynchronousUnaryClient GRPC_FINAL : public SynchronousClient {
     grpc::Status s =
         stub->UnaryCall(&context, request_, &responses_[thread_idx]);
     histogram->Add((Timer::Now() - start) * 1e9);
-    return s.IsOk();
+    return s.ok();
   }
 };
 
@@ -124,7 +124,7 @@ class SynchronousStreamingClient GRPC_FINAL : public SynchronousClient {
     for (auto stream = stream_.begin(); stream != stream_.end(); stream++) {
       if (*stream) {
         (*stream)->WritesDone();
-        EXPECT_TRUE((*stream)->Finish().IsOk());
+        EXPECT_TRUE((*stream)->Finish().ok());
       }
     }
   }
diff --git a/test/cpp/qps/driver.cc b/test/cpp/qps/driver.cc
index bf12730f97f00cf6f5128713d46d12c9438c58e8..c8cc11e6ab3483a4fccf3e579168439c7f64b330 100644
--- a/test/cpp/qps/driver.cc
+++ b/test/cpp/qps/driver.cc
@@ -241,11 +241,11 @@ std::unique_ptr<ScenarioResult> RunScenario(
 
   for (auto client = clients.begin(); client != clients.end(); client++) {
     GPR_ASSERT(client->stream->WritesDone());
-    GPR_ASSERT(client->stream->Finish().IsOk());
+    GPR_ASSERT(client->stream->Finish().ok());
   }
   for (auto server = servers.begin(); server != servers.end(); server++) {
     GPR_ASSERT(server->stream->WritesDone());
-    GPR_ASSERT(server->stream->Finish().IsOk());
+    GPR_ASSERT(server->stream->Finish().ok());
   }
   return result;
 }
diff --git a/test/cpp/qps/qps_worker.cc b/test/cpp/qps/qps_worker.cc
index 40cc4cb89bacd1711d9eeee7428e8aa14449fc9d..423275ee859dcdcc1bda495746d72d5a0b3c0e3e 100644
--- a/test/cpp/qps/qps_worker.cc
+++ b/test/cpp/qps/qps_worker.cc
@@ -100,7 +100,7 @@ class WorkerImpl GRPC_FINAL : public Worker::Service {
       GRPC_OVERRIDE {
     InstanceGuard g(this);
     if (!g.Acquired()) {
-      return Status(RESOURCE_EXHAUSTED);
+      return Status(StatusCode::RESOURCE_EXHAUSTED, "");
     }
 
     grpc_profiler_start("qps_client.prof");
@@ -114,7 +114,7 @@ class WorkerImpl GRPC_FINAL : public Worker::Service {
       GRPC_OVERRIDE {
     InstanceGuard g(this);
     if (!g.Acquired()) {
-      return Status(RESOURCE_EXHAUSTED);
+      return Status(StatusCode::RESOURCE_EXHAUSTED, "");
     }
 
     grpc_profiler_start("qps_server.prof");
@@ -159,22 +159,22 @@ class WorkerImpl GRPC_FINAL : public Worker::Service {
                      ServerReaderWriter<ClientStatus, ClientArgs>* stream) {
     ClientArgs args;
     if (!stream->Read(&args)) {
-      return Status(INVALID_ARGUMENT);
+      return Status(StatusCode::INVALID_ARGUMENT, "");
     }
     if (!args.has_setup()) {
-      return Status(INVALID_ARGUMENT);
+      return Status(StatusCode::INVALID_ARGUMENT, "");
     }
     auto client = CreateClient(args.setup());
     if (!client) {
-      return Status(INVALID_ARGUMENT);
+      return Status(StatusCode::INVALID_ARGUMENT, "");
     }
     ClientStatus status;
     if (!stream->Write(status)) {
-      return Status(UNKNOWN);
+      return Status(StatusCode::UNKNOWN, "");
     }
     while (stream->Read(&args)) {
       if (!args.has_mark()) {
-        return Status(INVALID_ARGUMENT);
+        return Status(StatusCode::INVALID_ARGUMENT, "");
       }
       *status.mutable_stats() = client->Mark();
       stream->Write(status);
@@ -187,23 +187,23 @@ class WorkerImpl GRPC_FINAL : public Worker::Service {
                        ServerReaderWriter<ServerStatus, ServerArgs>* stream) {
     ServerArgs args;
     if (!stream->Read(&args)) {
-      return Status(INVALID_ARGUMENT);
+      return Status(StatusCode::INVALID_ARGUMENT, "");
     }
     if (!args.has_setup()) {
-      return Status(INVALID_ARGUMENT);
+      return Status(StatusCode::INVALID_ARGUMENT, "");
     }
     auto server = CreateServer(args.setup(), server_port_);
     if (!server) {
-      return Status(INVALID_ARGUMENT);
+      return Status(StatusCode::INVALID_ARGUMENT, "");
     }
     ServerStatus status;
     status.set_port(server_port_);
     if (!stream->Write(status)) {
-      return Status(UNKNOWN);
+      return Status(StatusCode::UNKNOWN, "");
     }
     while (stream->Read(&args)) {
       if (!args.has_mark()) {
-        return Status(INVALID_ARGUMENT);
+        return Status(StatusCode::INVALID_ARGUMENT, "");
       }
       *status.mutable_stats() = server->Mark();
       stream->Write(status);
diff --git a/test/cpp/util/cli_call.cc b/test/cpp/util/cli_call.cc
index eb67b8d314bc2e21e11268a1d699402102b2ae10..83a7a1744a895035a5217f74c75bea9e672ec7ed 100644
--- a/test/cpp/util/cli_call.cc
+++ b/test/cpp/util/cli_call.cc
@@ -52,11 +52,20 @@ namespace {
 void* tag(int i) { return (void*)(gpr_intptr) i; }
 }  // namespace
 
-void CliCall::Call(std::shared_ptr<grpc::ChannelInterface> channel,
-                   const grpc::string& method, const grpc::string& request,
-                   grpc::string* response) {
+Status CliCall::Call(std::shared_ptr<grpc::ChannelInterface> channel,
+                     const grpc::string& method, const grpc::string& request,
+                     grpc::string* response, const MetadataContainer& metadata,
+                     MetadataContainer* server_initial_metadata,
+                     MetadataContainer* server_trailing_metadata) {
   std::unique_ptr<grpc::GenericStub> stub(new grpc::GenericStub(channel));
   grpc::ClientContext ctx;
+  if (!metadata.empty()) {
+    for (std::multimap<grpc::string, grpc::string>::const_iterator iter =
+             metadata.begin();
+         iter != metadata.end(); ++iter) {
+      ctx.AddMetadata(iter->first, iter->second);
+    }
+  }
   grpc::CompletionQueue cq;
   std::unique_ptr<grpc::GenericClientAsyncReaderWriter> call(
       stub->Call(&ctx, method, &cq, tag(1)));
@@ -79,15 +88,14 @@ void CliCall::Call(std::shared_ptr<grpc::ChannelInterface> channel,
   cq.Next(&got_tag, &ok);
   if (!ok) {
     std::cout << "Failed to read response." << std::endl;
-    return;
+    return Status(StatusCode::INTERNAL, "Failed to read response");
   }
   grpc::Status status;
   call->Finish(&status, tag(5));
   cq.Next(&got_tag, &ok);
   GPR_ASSERT(ok);
 
-  if (status.IsOk()) {
-    std::cout << "RPC finished with OK status." << std::endl;
+  if (status.ok()) {
     std::vector<grpc::Slice> slices;
     recv_buffer.Dump(&slices);
 
@@ -96,10 +104,10 @@ void CliCall::Call(std::shared_ptr<grpc::ChannelInterface> channel,
       response->append(reinterpret_cast<const char*>(slices[i].begin()),
                        slices[i].size());
     }
-  } else {
-    std::cout << "RPC finished with status code " << status.code()
-              << " details: " << status.details() << std::endl;
   }
+  *server_initial_metadata = ctx.GetServerInitialMetadata();
+  *server_trailing_metadata = ctx.GetServerTrailingMetadata();
+  return status;
 }
 
 }  // namespace testing
diff --git a/test/cpp/util/cli_call.h b/test/cpp/util/cli_call.h
index 7be8bb63c41846651eae2ca94861c618d4e87f73..8d114c9cb5ece4c5672630869170457247b366c7 100644
--- a/test/cpp/util/cli_call.h
+++ b/test/cpp/util/cli_call.h
@@ -34,17 +34,23 @@
 #ifndef GRPC_TEST_CPP_UTIL_CLI_CALL_H
 #define GRPC_TEST_CPP_UTIL_CLI_CALL_H
 
+#include <map>
+
 #include <grpc++/channel_interface.h>
 #include <grpc++/config.h>
+#include <grpc++/status.h>
 
 namespace grpc {
 namespace testing {
 
 class CliCall GRPC_FINAL {
  public:
-  static void Call(std::shared_ptr<grpc::ChannelInterface> channel,
-                   const grpc::string& method, const grpc::string& request,
-                   grpc::string* response);
+  typedef std::multimap<grpc::string, grpc::string> MetadataContainer;
+  static Status Call(std::shared_ptr<grpc::ChannelInterface> channel,
+                     const grpc::string& method, const grpc::string& request,
+                     grpc::string* response, const MetadataContainer& metadata,
+                     MetadataContainer* server_initial_metadata,
+                     MetadataContainer* server_trailing_metadata);
 };
 
 }  // namespace testing
diff --git a/test/cpp/util/cli_call_test.cc b/test/cpp/util/cli_call_test.cc
index 457a5e77de8d1b6d5f6263a5bb8b27f96936e433..6cf86ea89bf5eb70703eb42997878b8b052564d3 100644
--- a/test/cpp/util/cli_call_test.cc
+++ b/test/cpp/util/cli_call_test.cc
@@ -60,6 +60,14 @@ class TestServiceImpl : public ::grpc::cpp::test::util::TestService::Service {
  public:
   Status Echo(ServerContext* context, const EchoRequest* request,
               EchoResponse* response) GRPC_OVERRIDE {
+    if (!context->client_metadata().empty()) {
+      for (std::multimap<grpc::string, grpc::string>::const_iterator iter =
+               context->client_metadata().begin();
+           iter != context->client_metadata().end(); ++iter) {
+        context->AddInitialMetadata(iter->first, iter->second);
+      }
+    }
+    context->AddTrailingMetadata("trailing_key", "trailing_value");
     response->set_message(request->message());
     return Status::OK;
   }
@@ -106,16 +114,26 @@ TEST_F(CliCallTest, SimpleRpc) {
   request.set_message("Hello");
 
   ClientContext context;
+  context.AddMetadata("key1", "val1");
   Status s = stub_->Echo(&context, request, &response);
   EXPECT_EQ(response.message(), request.message());
-  EXPECT_TRUE(s.IsOk());
+  EXPECT_TRUE(s.ok());
 
   const grpc::string kMethod("/grpc.cpp.test.util.TestService/Echo");
   grpc::string request_bin, response_bin, expected_response_bin;
   EXPECT_TRUE(request.SerializeToString(&request_bin));
   EXPECT_TRUE(response.SerializeToString(&expected_response_bin));
-  CliCall::Call(channel_, kMethod, request_bin, &response_bin);
+  std::multimap<grpc::string, grpc::string> client_metadata,
+      server_initial_metadata, server_trailing_metadata;
+  client_metadata.insert(std::pair<grpc::string, grpc::string>("key1", "val1"));
+  Status s2 = CliCall::Call(channel_, kMethod, request_bin, &response_bin,
+                            client_metadata, &server_initial_metadata,
+                            &server_trailing_metadata);
+  EXPECT_TRUE(s2.ok());
+
   EXPECT_EQ(expected_response_bin, response_bin);
+  EXPECT_EQ(context.GetServerInitialMetadata(), server_initial_metadata);
+  EXPECT_EQ(context.GetServerTrailingMetadata(), server_trailing_metadata);
 }
 
 }  // namespace testing
diff --git a/test/cpp/util/grpc_cli.cc b/test/cpp/util/grpc_cli.cc
index ad3c0af8775980577e42db833bbd356b2f23f358..32d61b03077c80a0e2fe56b951e676e2290f49e5 100644
--- a/test/cpp/util/grpc_cli.cc
+++ b/test/cpp/util/grpc_cli.cc
@@ -41,8 +41,8 @@
           body: "hello world"
         }
     b. under grpc/ run
-        protoc --proto_path=test/cpp/interop/ \
-        --encode=grpc.testing.SimpleRequest test/cpp/interop/messages.proto \
+        protoc --proto_path=test/proto/ \
+        --encode=grpc.testing.SimpleRequest test/proto/messages.proto \
         < input.txt > input.bin
   2. Start a server
     make interop_server && bins/opt/interop_server --port=50051
@@ -51,10 +51,12 @@
     /grpc.testing.TestService/UnaryCall --enable_ssl=false \
     --input_binary_file=input.bin --output_binary_file=output.bin
   4. Decode response
-    protoc --proto_path=test/cpp/interop/ \
-    --decode=grpc.testing.SimpleResponse test/cpp/interop/messages.proto \
+    protoc --proto_path=test/proto/ \
+    --decode=grpc.testing.SimpleResponse test/proto/messages.proto \
     < output.bin > output.txt
   5. Now the text form of response should be in output.txt
+  Optionally, metadata can be passed to server via flag --metadata, e.g.
+    --metadata="MyHeaderKey1:Value1:MyHeaderKey2:Value2"
 */
 
 #include <fstream>
@@ -77,6 +79,44 @@ DEFINE_string(input_binary_file, "",
               "Path to input file containing serialized request.");
 DEFINE_string(output_binary_file, "output.bin",
               "Path to output file to write serialized response.");
+DEFINE_string(metadata, "",
+              "Metadata to send to server, in the form of key1:val1:key2:val2");
+
+void ParseMetadataFlag(
+    std::multimap<grpc::string, grpc::string>* client_metadata) {
+  if (FLAGS_metadata.empty()) {
+    return;
+  }
+  std::vector<grpc::string> fields;
+  grpc::string delim(":");
+  size_t cur, next = -1;
+  do {
+    cur = next + 1;
+    next = FLAGS_metadata.find_first_of(delim, cur);
+    fields.push_back(FLAGS_metadata.substr(cur, next - cur));
+  } while (next != grpc::string::npos);
+  if (fields.size() % 2) {
+    std::cout << "Failed to parse metadata flag" << std::endl;
+    exit(1);
+  }
+  for (size_t i = 0; i < fields.size(); i += 2) {
+    client_metadata->insert(
+        std::pair<grpc::string, grpc::string>(fields[i], fields[i + 1]));
+  }
+}
+
+void PrintMetadata(const std::multimap<grpc::string, grpc::string>& m,
+                   const grpc::string& message) {
+  if (m.empty()) {
+    return;
+  }
+  std::cout << message << std::endl;
+  for (std::multimap<grpc::string, grpc::string>::const_iterator iter =
+           m.begin();
+       iter != m.end(); ++iter) {
+    std::cout << iter->first << " : " << iter->second << std::endl;
+  }
+}
 
 int main(int argc, char** argv) {
   grpc::testing::InitTest(&argc, &argv, true);
@@ -118,11 +158,27 @@ int main(int argc, char** argv) {
       grpc::CreateChannel(server_address, creds, grpc::ChannelArguments());
 
   grpc::string response;
-  grpc::testing::CliCall::Call(channel, method, input_stream.str(), &response);
-  if (!response.empty()) {
-    std::ofstream output_file(FLAGS_output_binary_file,
-                              std::ios::trunc | std::ios::binary);
-    output_file << response;
+  std::multimap<grpc::string, grpc::string> client_metadata,
+      server_initial_metadata, server_trailing_metadata;
+  ParseMetadataFlag(&client_metadata);
+  PrintMetadata(client_metadata, "Sending client initial metadata:");
+  grpc::Status s = grpc::testing::CliCall::Call(
+      channel, method, input_stream.str(), &response, client_metadata,
+      &server_initial_metadata, &server_trailing_metadata);
+  PrintMetadata(server_initial_metadata,
+                "Received initial metadata from server:");
+  PrintMetadata(server_trailing_metadata,
+                "Received trailing metadata from server:");
+  if (s.ok()) {
+    std::cout << "Rpc succeeded with OK status" << std::endl;
+    if (!response.empty()) {
+      std::ofstream output_file(FLAGS_output_binary_file,
+                                std::ios::trunc | std::ios::binary);
+      output_file << response;
+    }
+  } else {
+    std::cout << "Rpc failed with status code " << s.error_code()
+              << " error message " << s.error_message() << std::endl;
   }
 
   return 0;
diff --git a/tools/doxygen/Doxyfile.c++ b/tools/doxygen/Doxyfile.c++
index cc2d7f46aeada0ca7c74860bfcaf5f643def27b5..5616f2c466890b90e7c44a5354f18119378acacf 100644
--- a/tools/doxygen/Doxyfile.c++
+++ b/tools/doxygen/Doxyfile.c++
@@ -40,7 +40,7 @@ PROJECT_NAME           = "GRPC C++"
 # could be handy for archiving the generated documentation or if some version
 # control system is used.
 
-PROJECT_NUMBER         = 0.9.1.0
+PROJECT_NUMBER         = 0.10.0.0
 
 # Using the PROJECT_BRIEF tag one can provide an optional one line description
 # for a project that appears at the top of each page and should give viewer a
diff --git a/tools/doxygen/Doxyfile.c++.internal b/tools/doxygen/Doxyfile.c++.internal
index 71e2d012bb0bcd9c95d546c6a6e84d80f92e0951..6d323274c913cb82b7b5f3fb5eb63e8a8138b802 100644
--- a/tools/doxygen/Doxyfile.c++.internal
+++ b/tools/doxygen/Doxyfile.c++.internal
@@ -40,7 +40,7 @@ PROJECT_NAME           = "GRPC C++"
 # could be handy for archiving the generated documentation or if some version
 # control system is used.
 
-PROJECT_NUMBER         = 0.9.1.0
+PROJECT_NUMBER         = 0.10.0.0
 
 # Using the PROJECT_BRIEF tag one can provide an optional one line description
 # for a project that appears at the top of each page and should give viewer a
diff --git a/tools/doxygen/Doxyfile.core b/tools/doxygen/Doxyfile.core
index a834728150028c4a48e387a8fb9a676dfd71f5f8..7cc96b2e06a337b32bc63b4f08a9adda540bb4b2 100644
--- a/tools/doxygen/Doxyfile.core
+++ b/tools/doxygen/Doxyfile.core
@@ -40,7 +40,7 @@ PROJECT_NAME           = "GRPC Core"
 # could be handy for archiving the generated documentation or if some version
 # control system is used.
 
-PROJECT_NUMBER         = 0.9.1.0
+PROJECT_NUMBER         = 0.10.0.0
 
 # Using the PROJECT_BRIEF tag one can provide an optional one line description
 # for a project that appears at the top of each page and should give viewer a
diff --git a/tools/doxygen/Doxyfile.core.internal b/tools/doxygen/Doxyfile.core.internal
index 8a79a67646b19097cc22a370d22dac018913487c..9aecfb9b9a5590ccf1f2922478527a8b3c77e463 100644
--- a/tools/doxygen/Doxyfile.core.internal
+++ b/tools/doxygen/Doxyfile.core.internal
@@ -40,7 +40,7 @@ PROJECT_NAME           = "GRPC Core"
 # could be handy for archiving the generated documentation or if some version
 # control system is used.
 
-PROJECT_NUMBER         = 0.9.1.0
+PROJECT_NUMBER         = 0.10.0.0
 
 # Using the PROJECT_BRIEF tag one can provide an optional one line description
 # for a project that appears at the top of each page and should give viewer a
diff --git a/tools/jenkins/grpc_jenkins_slave/Dockerfile b/tools/jenkins/grpc_jenkins_slave/Dockerfile
index c48bc0220f1ed71f0a49bc4724e48bc862248f60..b12c309e876ca47f181f7331f3e6ed20c30befb5 100644
--- a/tools/jenkins/grpc_jenkins_slave/Dockerfile
+++ b/tools/jenkins/grpc_jenkins_slave/Dockerfile
@@ -83,6 +83,7 @@ ENV NUGET mono /var/local/NuGet.exe
 # Node dependencies
 
 # Install nvm
+RUN touch .profile
 RUN curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.25.4/install.sh | bash
 RUN /bin/bash -l -c "nvm install 0.12"
 
diff --git a/tools/jenkins/run_jenkins.sh b/tools/jenkins/run_jenkins.sh
index 5e8ddb92fcf80ecf9f69d7e90c595aae764b6480..763a930dd8ca4ddef050d86e3ec200a3e14908a6 100755
--- a/tools/jenkins/run_jenkins.sh
+++ b/tools/jenkins/run_jenkins.sh
@@ -56,9 +56,11 @@ then
     $FETCH_PULL_REQUEST_CMD \
     && git checkout -f $GIT_COMMIT \
     && git submodule update \
+    && pip install simplejson mako \
     && nvm use 0.12 \
     && rvm use ruby-2.1 \
-    && tools/run_tests/run_tests.py -t -l $language" || DOCKER_FAILED="true"
+    && CONFIG=$config tools/run_tests/prepare_travis.sh \
+    && CPPFLAGS=-I/tmp/prebuilt/include tools/run_tests/run_tests.py -t -c $config -l $language" || DOCKER_FAILED="true"
 
   DOCKER_CID=`cat docker.cid`
   if [ "$DOCKER_FAILED" == "" ]
diff --git a/tools/run_tests/build_python.sh b/tools/run_tests/build_python.sh
index 53db6af0ea16942b15daabce3ca3a421beca3a45..d9b7644f449826d966be54a9ad456f3b50013658 100755
--- a/tools/run_tests/build_python.sh
+++ b/tools/run_tests/build_python.sh
@@ -38,5 +38,5 @@ rm -rf python2.7_virtual_environment
 virtualenv -p /usr/bin/python2.7 python2.7_virtual_environment
 source python2.7_virtual_environment/bin/activate
 pip install -r src/python/requirements.txt
-CFLAGS="-I$root/include -std=c89" LDFLAGS=-L$root/libs/$CONFIG pip install src/python/src
+CFLAGS="-I$root/include -std=c89 -Werror" LDFLAGS=-L$root/libs/$CONFIG pip install src/python/src
 pip install src/python/interop
diff --git a/tools/run_tests/prepare_travis.sh b/tools/run_tests/prepare_travis.sh
index 34a058f2a8b8eb99245db5da93b39a6275f7ac19..10546535e8ea10387f5545a309a58424b027dd48 100755
--- a/tools/run_tests/prepare_travis.sh
+++ b/tools/run_tests/prepare_travis.sh
@@ -32,17 +32,17 @@ cd `dirname $0`/../..
 grpc_dir=`pwd`
 
 distrib=`md5sum /etc/issue | cut -f1 -d\ `
-echo "Configuring for disbribution $distrib"
+echo "Configuring for distribution $distrib"
 git submodule | while read sha path extra ; do
   cd /tmp
   name=`basename $path`
   file=$name-$sha-$CONFIG-prebuilt-$distrib.tar.gz
-  echo -n "$file ..."
+  echo -n "Looking for $file ..."
   url=http://storage.googleapis.com/grpc-prebuilt-packages/$file
   wget -q $url && (
     echo " Found."
     tar xfz $file
-  ) || true
+  ) || echo " Not found."
 done
 
 mkdir -p bins/$CONFIG/protobuf