Merge pull request #14854 from rebello95/cpp-swift-warning

Resolve Swift warnings by specifying void arguments
diff --git a/doc/environment_variables.md b/doc/environment_variables.md
index 587ab83..6911e22 100644
--- a/doc/environment_variables.md
+++ b/doc/environment_variables.md
@@ -49,6 +49,9 @@
   - connectivity_state - traces connectivity state changes to channels
   - channel_stack_builder - traces information about channel stacks being built
   - executor - traces grpc's internal thread pool ('the executor')
+  - fd_trace - traces fd create(), shutdown() and close() calls for channel fds.
+    Also traces epoll fd create()/close() calls in epollex polling engine
+    traces epoll-fd creation/close calls for epollex polling engine
   - glb - traces the grpclb load balancer
   - handshaker - traces handshaking state
   - http - traces state in the http2 transport engine
diff --git a/doc/fork_support.md b/doc/fork_support.md
new file mode 100644
index 0000000..d0f59f2
--- /dev/null
+++ b/doc/fork_support.md
@@ -0,0 +1,46 @@
+# Background #
+
+In Python, multithreading is ineffective at concurrency for CPU bound tasks
+due to the GIL (global interpreter lock).  Extension modules can release
+the GIL in CPU bound tasks, but that isn't an option in pure Python.
+Users use libraries such as multiprocessing, subprocess, concurrent.futures.ProcessPoolExecutor,
+etc, to work around the GIL. These modules call ```fork()``` underneath the hood. Various issues have
+been reported when using these modules with gRPC Python.  gRPC Python wraps
+gRPC core, which uses multithreading for performance, and hence doesn't support ```fork()```.
+Historically, we didn't support forking in gRPC, but some users seemed
+to be doing fine until their code started to break on version 1.6.  This was
+likely caused by the addition of background c-threads and a background
+Python thread.
+
+# Current Status #
+
+## 1.11 ##
+The background Python thread was removed entirely.  This allows forking
+after creating a channel.  However, the channel must not have issued any
+RPCs prior to the fork.  Attempting to fork with an active channel that
+has been used can result in deadlocks/corrupted wire data.
+
+## 1.9 ##
+A regression was noted in cases where users are doing fork/exec. This
+was due to ```pthread_atfork()``` handler that was added in 1.7 to partially
+support forking in gRPC. A deadlock can happen when pthread_atfork
+handler is running, and an application thread is calling into gRPC.
+We have provided a workaround for this issue by allowing users to turn 
+off the handler using env flag ```GRPC_ENABLE_FORK_SUPPORT=False```.
+This should be set whenever a user expects to always call exec
+immediately following fork.  It will disable the fork handlers.
+
+## 1.7 ##
+A ```pthread_atfork()``` handler was added in 1.7 to automatically shut down
+the background c-threads when fork was called.  This does not shut down the
+background Python thread, so users could not have any open channels when
+forking.
+
+# Future Work #
+
+## 1.13 ##
+The workaround when using fork/exec by setting
+```GRPC_ENABLE_FORK_SUPPORT=False``` should no longer be needed.  Following
+[this PR](https://github.com/grpc/grpc/pull/14647), fork
+handlers will not automatically run when multiple threads are calling
+into gRPC.
diff --git a/src/core/ext/filters/client_channel/channel_connectivity.cc b/src/core/ext/filters/client_channel/channel_connectivity.cc
index 37860e8..c71d102 100644
--- a/src/core/ext/filters/client_channel/channel_connectivity.cc
+++ b/src/core/ext/filters/client_channel/channel_connectivity.cc
@@ -40,7 +40,7 @@
   GRPC_API_TRACE(
       "grpc_channel_check_connectivity_state(channel=%p, try_to_connect=%d)", 2,
       (channel, try_to_connect));
-  if (client_channel_elem->filter == &grpc_client_channel_filter) {
+  if (GPR_LIKELY(client_channel_elem->filter == &grpc_client_channel_filter)) {
     state = grpc_client_channel_check_connectivity_state(client_channel_elem,
                                                          try_to_connect);
 
diff --git a/src/core/ext/filters/client_channel/client_channel.cc b/src/core/ext/filters/client_channel/client_channel.cc
index 80a647f..3813190 100644
--- a/src/core/ext/filters/client_channel/client_channel.cc
+++ b/src/core/ext/filters/client_channel/client_channel.cc
@@ -379,7 +379,7 @@
         new_lb_policy =
             grpc_core::LoadBalancingPolicyRegistry::CreateLoadBalancingPolicy(
                 lb_policy_name, lb_policy_args);
-        if (new_lb_policy == nullptr) {
+        if (GPR_UNLIKELY(new_lb_policy == nullptr)) {
           gpr_log(GPR_ERROR, "could not create LB policy \"%s\"",
                   lb_policy_name);
         } else {
@@ -2200,7 +2200,7 @@
         &batch_data->send_initial_metadata_storage[calld->send_initial_metadata
                                                        .list.count],
         retry_md);
-    if (error != GRPC_ERROR_NONE) {
+    if (GPR_UNLIKELY(error != GRPC_ERROR_NONE)) {
       gpr_log(GPR_ERROR, "error adding retry metadata: %s",
               grpc_error_string(error));
       GPR_ASSERT(false);
diff --git a/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.cc b/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.cc
index 70a91b2..8bf739c 100644
--- a/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.cc
+++ b/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.cc
@@ -399,7 +399,7 @@
 bool IsServerValid(const grpc_grpclb_server* server, size_t idx, bool log) {
   if (server->drop) return false;
   const grpc_grpclb_ip_address* ip = &server->ip_address;
-  if (server->port >> 16 != 0) {
+  if (GPR_UNLIKELY(server->port >> 16 != 0)) {
     if (log) {
       gpr_log(GPR_ERROR,
               "Invalid port '%d' at index %lu of serverlist. Ignoring.",
@@ -407,7 +407,7 @@
     }
     return false;
   }
-  if (ip->size != 4 && ip->size != 16) {
+  if (GPR_UNLIKELY(ip->size != 4 && ip->size != 16)) {
     if (log) {
       gpr_log(GPR_ERROR,
               "Expected IP to be 4 or 16 bytes, got %d at index %lu of "
@@ -715,7 +715,7 @@
                     this, grpc_combiner_scheduler(grpclb_policy()->combiner()));
   grpc_call_error call_error = grpc_call_start_batch_and_execute(
       lb_call_, &op, 1, &client_load_report_closure_);
-  if (call_error != GRPC_CALL_OK) {
+  if (GPR_UNLIKELY(call_error != GRPC_CALL_OK)) {
     gpr_log(GPR_ERROR, "[grpclb %p] call_error=%d", grpclb_policy_.get(),
             call_error);
     GPR_ASSERT(GRPC_CALL_OK == call_error);
@@ -937,7 +937,7 @@
   size_t lb_addresses_idx = 0;
   for (size_t i = 0; i < addresses->num_addresses; ++i) {
     if (!addresses->addresses[i].is_balancer) continue;
-    if (addresses->addresses[i].user_data != nullptr) {
+    if (GPR_UNLIKELY(addresses->addresses[i].user_data != nullptr)) {
       gpr_log(GPR_ERROR,
               "This LB policy doesn't support user data. It will be ignored");
     }
@@ -1288,7 +1288,7 @@
 
 void GrpcLb::ProcessChannelArgsLocked(const grpc_channel_args& args) {
   const grpc_arg* arg = grpc_channel_args_find(&args, GRPC_ARG_LB_ADDRESSES);
-  if (arg == nullptr || arg->type != GRPC_ARG_POINTER) {
+  if (GPR_UNLIKELY(arg == nullptr || arg->type != GRPC_ARG_POINTER)) {
     // Ignore this update.
     gpr_log(
         GPR_ERROR,
@@ -1524,7 +1524,7 @@
    * policy (e.g., all addresses failed to connect). There won't be any
    * user_data/token available */
   if (pp->pick->connected_subchannel != nullptr) {
-    if (!GRPC_MDISNULL(pp->lb_token)) {
+    if (GPR_LIKELY(!GRPC_MDISNULL(pp->lb_token))) {
       AddLbTokenToInitialMetadata(GRPC_MDELEM_REF(pp->lb_token),
                                   &pp->pick->lb_token_mdelem_storage,
                                   pp->pick->initial_metadata);
@@ -1649,7 +1649,7 @@
   GPR_ASSERT(rr_policy_ == nullptr);
   rr_policy_ = LoadBalancingPolicyRegistry::CreateLoadBalancingPolicy(
       "round_robin", args);
-  if (rr_policy_ == nullptr) {
+  if (GPR_UNLIKELY(rr_policy_ == nullptr)) {
     gpr_log(GPR_ERROR, "[grpclb %p] Failure creating a RoundRobin policy",
             this);
     return;
diff --git a/src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.cc b/src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.cc
index 7ef3bcf..0e80dd5 100644
--- a/src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.cc
+++ b/src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.cc
@@ -29,7 +29,7 @@
                              void** arg) {
   grpc_grpclb_serverlist* sl = static_cast<grpc_grpclb_serverlist*>(*arg);
   grpc_grpclb_server server;
-  if (!pb_decode(stream, grpc_lb_v1_Server_fields, &server)) {
+  if (GPR_UNLIKELY(!pb_decode(stream, grpc_lb_v1_Server_fields, &server))) {
     gpr_log(GPR_ERROR, "nanopb error: %s", PB_GET_ERROR(stream));
     return false;
   }
@@ -52,7 +52,7 @@
   GPR_ASSERT(dec_arg->serverlist->num_servers >= dec_arg->decoding_idx);
   grpc_grpclb_server* server =
       static_cast<grpc_grpclb_server*>(gpr_zalloc(sizeof(grpc_grpclb_server)));
-  if (!pb_decode(stream, grpc_lb_v1_Server_fields, server)) {
+  if (GPR_UNLIKELY(!pb_decode(stream, grpc_lb_v1_Server_fields, server))) {
     gpr_free(server);
     gpr_log(GPR_ERROR, "nanopb error: %s", PB_GET_ERROR(stream));
     return false;
@@ -165,7 +165,8 @@
                              GRPC_SLICE_LENGTH(encoded_grpc_grpclb_response));
   grpc_grpclb_response res;
   memset(&res, 0, sizeof(grpc_grpclb_response));
-  if (!pb_decode(&stream, grpc_lb_v1_LoadBalanceResponse_fields, &res)) {
+  if (GPR_UNLIKELY(
+          !pb_decode(&stream, grpc_lb_v1_LoadBalanceResponse_fields, &res))) {
     gpr_log(GPR_ERROR, "nanopb error: %s", PB_GET_ERROR(&stream));
     return nullptr;
   }
@@ -195,7 +196,7 @@
   res.server_list.servers.funcs.decode = count_serverlist;
   res.server_list.servers.arg = sl;
   bool status = pb_decode(&stream, grpc_lb_v1_LoadBalanceResponse_fields, &res);
-  if (!status) {
+  if (GPR_UNLIKELY(!status)) {
     gpr_free(sl);
     gpr_log(GPR_ERROR, "nanopb error: %s", PB_GET_ERROR(&stream));
     return nullptr;
@@ -211,7 +212,7 @@
     res.server_list.servers.arg = &decode_arg;
     status = pb_decode(&stream_at_start, grpc_lb_v1_LoadBalanceResponse_fields,
                        &res);
-    if (!status) {
+    if (GPR_UNLIKELY(!status)) {
       grpc_grpclb_destroy_serverlist(sl);
       gpr_log(GPR_ERROR, "nanopb error: %s", PB_GET_ERROR(&stream));
       return nullptr;
diff --git a/src/core/ext/filters/client_channel/lb_policy/round_robin/round_robin.cc b/src/core/ext/filters/client_channel/lb_policy/round_robin/round_robin.cc
index 79e8ad5..b177385 100644
--- a/src/core/ext/filters/client_channel/lb_policy/round_robin/round_robin.cc
+++ b/src/core/ext/filters/client_channel/lb_policy/round_robin/round_robin.cc
@@ -608,7 +608,7 @@
 
 void RoundRobin::UpdateLocked(const grpc_channel_args& args) {
   const grpc_arg* arg = grpc_channel_args_find(&args, GRPC_ARG_LB_ADDRESSES);
-  if (arg == nullptr || arg->type != GRPC_ARG_POINTER) {
+  if (GPR_UNLIKELY(arg == nullptr || arg->type != GRPC_ARG_POINTER)) {
     gpr_log(GPR_ERROR, "[RR %p] update provided no addresses; ignoring", this);
     // If we don't have a current subchannel list, go into TRANSIENT_FAILURE.
     // Otherwise, keep using the current subchannel list (ignore this update).
diff --git a/src/core/ext/filters/client_channel/resolver/dns/native/dns_resolver.cc b/src/core/ext/filters/client_channel/resolver/dns/native/dns_resolver.cc
index e7842a7..920a492 100644
--- a/src/core/ext/filters/client_channel/resolver/dns/native/dns_resolver.cc
+++ b/src/core/ext/filters/client_channel/resolver/dns/native/dns_resolver.cc
@@ -310,7 +310,7 @@
  public:
   OrphanablePtr<Resolver> CreateResolver(
       const ResolverArgs& args) const override {
-    if (0 != strcmp(args.uri->authority, "")) {
+    if (GPR_UNLIKELY(0 != strcmp(args.uri->authority, ""))) {
       gpr_log(GPR_ERROR, "authority based dns uri's not supported");
       return OrphanablePtr<Resolver>(nullptr);
     }
diff --git a/src/core/ext/filters/client_channel/subchannel.cc b/src/core/ext/filters/client_channel/subchannel.cc
index d7815fb..140441d 100644
--- a/src/core/ext/filters/client_channel/subchannel.cc
+++ b/src/core/ext/filters/client_channel/subchannel.cc
@@ -803,7 +803,7 @@
   };
   grpc_error* error = grpc_call_stack_init(
       channel_stack_, 1, subchannel_call_destroy, *call, &call_args);
-  if (error != GRPC_ERROR_NONE) {
+  if (GPR_UNLIKELY(error != GRPC_ERROR_NONE)) {
     const char* error_string = grpc_error_string(error);
     gpr_log(GPR_ERROR, "error: %s", error_string);
     return error;
diff --git a/src/core/ext/filters/http/message_compress/message_compress_filter.cc b/src/core/ext/filters/http/message_compress/message_compress_filter.cc
index f8f478b..933fe3c 100644
--- a/src/core/ext/filters/http/message_compress/message_compress_filter.cc
+++ b/src/core/ext/filters/http/message_compress/message_compress_filter.cc
@@ -116,8 +116,8 @@
   if (initial_metadata->idx.named.grpc_internal_encoding_request != nullptr) {
     grpc_mdelem md =
         initial_metadata->idx.named.grpc_internal_encoding_request->md;
-    if (!grpc_compression_algorithm_parse(GRPC_MDVALUE(md),
-                                          &compression_algorithm)) {
+    if (GPR_UNLIKELY(!grpc_compression_algorithm_parse(
+            GRPC_MDVALUE(md), &compression_algorithm))) {
       char* val = grpc_slice_to_c_string(GRPC_MDVALUE(md));
       gpr_log(GPR_ERROR,
               "Invalid compression algorithm: '%s' (unknown). Ignoring.", val);
@@ -125,8 +125,8 @@
       calld->message_compression_algorithm = GRPC_MESSAGE_COMPRESS_NONE;
       stream_compression_algorithm = GRPC_STREAM_COMPRESS_NONE;
     }
-    if (!GPR_BITGET(channeld->enabled_algorithms_bitset,
-                    compression_algorithm)) {
+    if (GPR_UNLIKELY(!GPR_BITGET(channeld->enabled_algorithms_bitset,
+                                 compression_algorithm))) {
       char* val = grpc_slice_to_c_string(GRPC_MDVALUE(md));
       gpr_log(GPR_ERROR,
               "Invalid compression algorithm: '%s' (previously disabled). "
diff --git a/src/core/ext/transport/chttp2/transport/bin_decoder.cc b/src/core/ext/transport/chttp2/transport/bin_decoder.cc
index f0f32da..b660a45 100644
--- a/src/core/ext/transport/chttp2/transport/bin_decoder.cc
+++ b/src/core/ext/transport/chttp2/transport/bin_decoder.cc
@@ -55,7 +55,7 @@
   size_t i;
 
   for (i = 0; i < length; ++i) {
-    if ((decode_table[input_ptr[i]] & 0xC0) != 0) {
+    if (GPR_UNLIKELY((decode_table[input_ptr[i]] & 0xC0) != 0)) {
       gpr_log(GPR_ERROR,
               "Base64 decoding failed, invalid character '%c' in base64 "
               "input.\n",
@@ -86,14 +86,14 @@
   while (len > 0 && bytes[len - 1] == '=') {
     len--;
   }
-  if (GRPC_SLICE_LENGTH(slice) - len > 2) {
+  if (GPR_UNLIKELY(GRPC_SLICE_LENGTH(slice) - len > 2)) {
     gpr_log(GPR_ERROR,
             "Base64 decoding failed. Input has more than 2 paddings.");
     return 0;
   }
   size_t tuples = len / 4;
   size_t tail_case = len % 4;
-  if (tail_case == 1) {
+  if (GPR_UNLIKELY(tail_case == 1)) {
     gpr_log(GPR_ERROR,
             "Base64 decoding failed. Input has a length of %zu (without"
             " padding), which is invalid.\n",
@@ -164,7 +164,7 @@
   struct grpc_base64_decode_context ctx;
   grpc_slice output;
 
-  if (input_length % 4 != 0) {
+  if (GPR_UNLIKELY(input_length % 4 != 0)) {
     gpr_log(GPR_ERROR,
             "Base64 decoding failed, input of "
             "grpc_chttp2_base64_decode has a length of %d, which is not a "
@@ -190,7 +190,7 @@
   ctx.output_end = GRPC_SLICE_END_PTR(output);
   ctx.contains_tail = false;
 
-  if (!grpc_base64_decode_partial(&ctx)) {
+  if (GPR_UNLIKELY(!grpc_base64_decode_partial(&ctx))) {
     char* s = grpc_slice_to_c_string(input);
     gpr_log(GPR_ERROR, "Base64 decoding failed, input string:\n%s\n", s);
     gpr_free(s);
@@ -209,7 +209,7 @@
   struct grpc_base64_decode_context ctx;
 
   // The length of a base64 string cannot be 4 * n + 1
-  if (input_length % 4 == 1) {
+  if (GPR_UNLIKELY(input_length % 4 == 1)) {
     gpr_log(GPR_ERROR,
             "Base64 decoding failed, input of "
             "grpc_chttp2_base64_decode_with_length has a length of %d, which "
@@ -219,7 +219,8 @@
     return grpc_empty_slice();
   }
 
-  if (output_length > input_length / 4 * 3 + tail_xtra[input_length % 4]) {
+  if (GPR_UNLIKELY(output_length >
+                   input_length / 4 * 3 + tail_xtra[input_length % 4])) {
     gpr_log(
         GPR_ERROR,
         "Base64 decoding failed, output_length %d is longer "
@@ -236,7 +237,7 @@
   ctx.output_end = GRPC_SLICE_END_PTR(output);
   ctx.contains_tail = true;
 
-  if (!grpc_base64_decode_partial(&ctx)) {
+  if (GPR_UNLIKELY(!grpc_base64_decode_partial(&ctx))) {
     char* s = grpc_slice_to_c_string(input);
     gpr_log(GPR_ERROR, "Base64 decoding failed, input string:\n%s\n", s);
     gpr_free(s);
diff --git a/src/core/ext/transport/chttp2/transport/chttp2_transport.cc b/src/core/ext/transport/chttp2/transport/chttp2_transport.cc
index 0ef7396..7aa7be4 100644
--- a/src/core/ext/transport/chttp2/transport/chttp2_transport.cc
+++ b/src/core/ext/transport/chttp2/transport/chttp2_transport.cc
@@ -718,7 +718,7 @@
   grpc_chttp2_list_remove_stalled_by_stream(t, s);
 
   for (int i = 0; i < STREAM_LIST_COUNT; i++) {
-    if (s->included[i]) {
+    if (GPR_UNLIKELY(s->included[i])) {
       gpr_log(GPR_ERROR, "%s stream %d still included in list %d",
               t->is_client ? "client" : "server", s->id, i);
       abort();
@@ -1088,8 +1088,9 @@
    * data equal to "too_many_pings", it should log the occurrence at a log level
    * that is enabled by default and double the configured KEEPALIVE_TIME used
    * for new connections on that channel. */
-  if (t->is_client && goaway_error == GRPC_HTTP2_ENHANCE_YOUR_CALM &&
-      grpc_slice_str_cmp(goaway_text, "too_many_pings") == 0) {
+  if (GPR_UNLIKELY(t->is_client &&
+                   goaway_error == GRPC_HTTP2_ENHANCE_YOUR_CALM &&
+                   grpc_slice_str_cmp(goaway_text, "too_many_pings") == 0)) {
     gpr_log(GPR_ERROR,
             "Received a GOAWAY with error code ENHANCE_YOUR_CALM and debug "
             "data equal to \"too_many_pings\"");
@@ -2701,7 +2702,7 @@
   } else {
     /* The watchdog timer should have been cancelled by
      * finish_keepalive_ping_locked. */
-    if (error != GRPC_ERROR_CANCELLED) {
+    if (GPR_UNLIKELY(error != GRPC_ERROR_CANCELLED)) {
       gpr_log(GPR_ERROR, "keepalive_ping_end state error: %d (expect: %d)",
               t->keepalive_state, GRPC_CHTTP2_KEEPALIVE_STATE_PINGING);
     }
diff --git a/src/core/ext/transport/chttp2/transport/parsing.cc b/src/core/ext/transport/chttp2/transport/parsing.cc
index a10c9ad..1e491d2 100644
--- a/src/core/ext/transport/chttp2/transport/parsing.cc
+++ b/src/core/ext/transport/chttp2/transport/parsing.cc
@@ -422,7 +422,8 @@
     if (cached_timeout != nullptr) {
       timeout = *cached_timeout;
     } else {
-      if (!grpc_http2_decode_timeout(GRPC_MDVALUE(md), &timeout)) {
+      if (GPR_UNLIKELY(
+              !grpc_http2_decode_timeout(GRPC_MDVALUE(md), &timeout))) {
         char* val = grpc_slice_to_c_string(GRPC_MDVALUE(md));
         gpr_log(GPR_ERROR, "Ignoring bad timeout value '%s'", val);
         gpr_free(val);
@@ -550,15 +551,15 @@
   /* could be a new grpc_chttp2_stream or an existing grpc_chttp2_stream */
   s = grpc_chttp2_parsing_lookup_stream(t, t->incoming_stream_id);
   if (s == nullptr) {
-    if (is_continuation) {
+    if (GPR_UNLIKELY(is_continuation)) {
       GRPC_CHTTP2_IF_TRACING(
           gpr_log(GPR_ERROR,
                   "grpc_chttp2_stream disbanded before CONTINUATION received"));
       return init_skip_frame_parser(t, 1);
     }
     if (t->is_client) {
-      if ((t->incoming_stream_id & 1) &&
-          t->incoming_stream_id < t->next_stream_id) {
+      if (GPR_LIKELY((t->incoming_stream_id & 1) &&
+                     t->incoming_stream_id < t->next_stream_id)) {
         /* this is an old (probably cancelled) grpc_chttp2_stream */
       } else {
         GRPC_CHTTP2_IF_TRACING(gpr_log(
@@ -569,7 +570,7 @@
         grpc_chttp2_hpack_parser_set_has_priority(&t->hpack_parser);
       }
       return err;
-    } else if (t->last_new_stream_id >= t->incoming_stream_id) {
+    } else if (GPR_UNLIKELY(t->last_new_stream_id >= t->incoming_stream_id)) {
       GRPC_CHTTP2_IF_TRACING(gpr_log(
           GPR_ERROR,
           "ignoring out of order new grpc_chttp2_stream request on server; "
@@ -577,21 +578,22 @@
           "id=%d, new grpc_chttp2_stream id=%d",
           t->last_new_stream_id, t->incoming_stream_id));
       return init_skip_frame_parser(t, 1);
-    } else if ((t->incoming_stream_id & 1) == 0) {
+    } else if (GPR_UNLIKELY((t->incoming_stream_id & 1) == 0)) {
       GRPC_CHTTP2_IF_TRACING(gpr_log(
           GPR_ERROR,
           "ignoring grpc_chttp2_stream with non-client generated index %d",
           t->incoming_stream_id));
       return init_skip_frame_parser(t, 1);
-    } else if (grpc_chttp2_stream_map_size(&t->stream_map) >=
-               t->settings[GRPC_ACKED_SETTINGS]
-                          [GRPC_CHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS]) {
+    } else if (GPR_UNLIKELY(
+                   grpc_chttp2_stream_map_size(&t->stream_map) >=
+                   t->settings[GRPC_ACKED_SETTINGS]
+                              [GRPC_CHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS])) {
       return GRPC_ERROR_CREATE_FROM_STATIC_STRING("Max stream count exceeded");
     }
     t->last_new_stream_id = t->incoming_stream_id;
     s = t->incoming_stream =
         grpc_chttp2_parsing_accept_stream(t, t->incoming_stream_id);
-    if (s == nullptr) {
+    if (GPR_UNLIKELY(s == nullptr)) {
       GRPC_CHTTP2_IF_TRACING(
           gpr_log(GPR_ERROR, "grpc_chttp2_stream not accepted"));
       return init_skip_frame_parser(t, 1);
@@ -601,7 +603,7 @@
   }
   GPR_ASSERT(s != nullptr);
   s->stats.incoming.framing_bytes += 9;
-  if (s->read_closed) {
+  if (GPR_UNLIKELY(s->read_closed)) {
     GRPC_CHTTP2_IF_TRACING(gpr_log(
         GPR_ERROR, "skipping already closed grpc_chttp2_stream header"));
     t->incoming_stream = nullptr;
@@ -723,7 +725,7 @@
                                      int is_last) {
   grpc_chttp2_stream* s = t->incoming_stream;
   grpc_error* err = t->parser(t->parser_data, t, s, slice, is_last);
-  if (err == GRPC_ERROR_NONE) {
+  if (GPR_LIKELY(err == GRPC_ERROR_NONE)) {
     return err;
   } else if (grpc_error_get_int(err, GRPC_ERROR_INT_STREAM_ID, nullptr)) {
     if (grpc_http_trace.enabled()) {
diff --git a/src/core/ext/transport/chttp2/transport/writing.cc b/src/core/ext/transport/chttp2/transport/writing.cc
index 85efe27..34d5e6e 100644
--- a/src/core/ext/transport/chttp2/transport/writing.cc
+++ b/src/core/ext/transport/chttp2/transport/writing.cc
@@ -337,10 +337,10 @@
          s_->fetching_send_message == nullptr);
     if (is_last_data_frame && s_->send_trailing_metadata != nullptr &&
         s_->stream_compression_ctx != nullptr) {
-      if (!grpc_stream_compress(
+      if (GPR_UNLIKELY(!grpc_stream_compress(
               s_->stream_compression_ctx, &s_->flow_controlled_buffer,
               &s_->compressed_data_buffer, nullptr, MAX_SIZE_T,
-              GRPC_STREAM_COMPRESSION_FLUSH_FINISH)) {
+              GRPC_STREAM_COMPRESSION_FLUSH_FINISH))) {
         gpr_log(GPR_ERROR, "Stream compression failed.");
       }
       grpc_stream_compression_context_destroy(s_->stream_compression_ctx);
@@ -368,10 +368,10 @@
           grpc_stream_compression_context_create(s_->stream_compression_method);
     }
     s_->uncompressed_data_size = s_->flow_controlled_buffer.length;
-    if (!grpc_stream_compress(s_->stream_compression_ctx,
-                              &s_->flow_controlled_buffer,
-                              &s_->compressed_data_buffer, nullptr, MAX_SIZE_T,
-                              GRPC_STREAM_COMPRESSION_FLUSH_SYNC)) {
+    if (GPR_UNLIKELY(!grpc_stream_compress(
+            s_->stream_compression_ctx, &s_->flow_controlled_buffer,
+            &s_->compressed_data_buffer, nullptr, MAX_SIZE_T,
+            GRPC_STREAM_COMPRESSION_FLUSH_SYNC))) {
       gpr_log(GPR_ERROR, "Stream compression failed.");
     }
   }
diff --git a/src/core/ext/transport/cronet/transport/cronet_transport.cc b/src/core/ext/transport/cronet/transport/cronet_transport.cc
index 8e3ea05..0b88ff7 100644
--- a/src/core/ext/transport/cronet/transport/cronet_transport.cc
+++ b/src/core/ext/transport/cronet/transport/cronet_transport.cc
@@ -360,7 +360,7 @@
                    s->storage.num_pending_ops);
         gpr_free(oas);
         break;
-      } else if (curr->next == nullptr) {
+      } else if (GPR_UNLIKELY(curr->next == nullptr)) {
         CRONET_LOG(GPR_ERROR, "Reached end of LL and did not find op to free");
       }
     }
@@ -1054,7 +1054,7 @@
         GPR_ASSERT(false);
       }
       grpc_slice_buffer_add(&write_slice_buffer, slice);
-      if (write_slice_buffer.count != 1) {
+      if (GPR_UNLIKELY(write_slice_buffer.count != 1)) {
         /* Empty request not handled yet */
         gpr_log(GPR_ERROR, "Empty request is not supported");
         GPR_ASSERT(write_slice_buffer.count == 1);
@@ -1455,7 +1455,7 @@
     for (size_t i = 0; i < args->num_args; i++) {
       if (0 ==
           strcmp(args->args[i].key, GRPC_ARG_USE_CRONET_PACKET_COALESCING)) {
-        if (args->args[i].type != GRPC_ARG_INTEGER) {
+        if (GPR_UNLIKELY(args->args[i].type != GRPC_ARG_INTEGER)) {
           gpr_log(GPR_ERROR, "%s ignored: it must be an integer",
                   GRPC_ARG_USE_CRONET_PACKET_COALESCING);
         } else {
diff --git a/src/core/lib/iomgr/ev_epollex_linux.cc b/src/core/lib/iomgr/ev_epollex_linux.cc
index 65f1c91..98369dd 100644
--- a/src/core/lib/iomgr/ev_epollex_linux.cc
+++ b/src/core/lib/iomgr/ev_epollex_linux.cc
@@ -447,9 +447,13 @@
   if (epfd == -1) {
     return GRPC_OS_ERROR(errno, "epoll_create1");
   }
+  GRPC_FD_TRACE("Pollable_create: created epfd: %d (type: %d)", epfd, type);
   *p = static_cast<pollable*>(gpr_malloc(sizeof(**p)));
   grpc_error* err = grpc_wakeup_fd_init(&(*p)->wakeup);
   if (err != GRPC_ERROR_NONE) {
+    GRPC_FD_TRACE(
+        "Pollable_create: closed epfd: %d (type: %d). wakeupfd_init error",
+        epfd, type);
     close(epfd);
     gpr_free(*p);
     *p = nullptr;
@@ -460,6 +464,9 @@
   ev.data.ptr = (void*)(1 | (intptr_t) & (*p)->wakeup);
   if (epoll_ctl(epfd, EPOLL_CTL_ADD, (*p)->wakeup.read_fd, &ev) != 0) {
     err = GRPC_OS_ERROR(errno, "epoll_ctl");
+    GRPC_FD_TRACE(
+        "Pollable_create: closed epfd: %d (type: %d). epoll_ctl error", epfd,
+        type);
     close(epfd);
     grpc_wakeup_fd_destroy(&(*p)->wakeup);
     gpr_free(*p);
@@ -506,6 +513,7 @@
   }
 #endif
   if (p != nullptr && gpr_unref(&p->refs)) {
+    GRPC_FD_TRACE("pollable_unref: Closing epfd: %d", p->epfd);
     close(p->epfd);
     grpc_wakeup_fd_destroy(&p->wakeup);
     gpr_free(p);
diff --git a/src/core/lib/iomgr/ev_posix.cc b/src/core/lib/iomgr/ev_posix.cc
index 4ea63fc..6bd1dc8 100644
--- a/src/core/lib/iomgr/ev_posix.cc
+++ b/src/core/lib/iomgr/ev_posix.cc
@@ -40,6 +40,9 @@
 
 grpc_core::TraceFlag grpc_polling_trace(false,
                                         "polling"); /* Disabled by default */
+
+/* Traces fd create/close operations */
+grpc_core::TraceFlag grpc_fd_trace(false, "fd_trace");
 grpc_core::DebugOnlyTraceFlag grpc_trace_fd_refcount(false, "fd_refcount");
 grpc_core::DebugOnlyTraceFlag grpc_polling_api_trace(false, "polling_api");
 
@@ -192,6 +195,7 @@
 
 grpc_fd* grpc_fd_create(int fd, const char* name) {
   GRPC_POLLING_API_TRACE("fd_create(%d, %s)", fd, name);
+  GRPC_FD_TRACE("fd_create(%d, %s)", fd, name);
   return g_event_engine->fd_create(fd, name);
 }
 
@@ -204,11 +208,14 @@
   GRPC_POLLING_API_TRACE("fd_orphan(%d, %p, %p, %d, %s)",
                          grpc_fd_wrapped_fd(fd), on_done, release_fd,
                          already_closed, reason);
+  GRPC_FD_TRACE("grpc_fd_orphan, fd:%d closed", grpc_fd_wrapped_fd(fd));
+
   g_event_engine->fd_orphan(fd, on_done, release_fd, already_closed, reason);
 }
 
 void grpc_fd_shutdown(grpc_fd* fd, grpc_error* why) {
   GRPC_POLLING_API_TRACE("fd_shutdown(%d)", grpc_fd_wrapped_fd(fd));
+  GRPC_FD_TRACE("fd_shutdown(%d)", grpc_fd_wrapped_fd(fd));
   g_event_engine->fd_shutdown(fd, why);
 }
 
diff --git a/src/core/lib/iomgr/ev_posix.h b/src/core/lib/iomgr/ev_posix.h
index 6a5129a..82cbce9 100644
--- a/src/core/lib/iomgr/ev_posix.h
+++ b/src/core/lib/iomgr/ev_posix.h
@@ -29,8 +29,14 @@
 #include "src/core/lib/iomgr/pollset_set.h"
 #include "src/core/lib/iomgr/wakeup_fd_posix.h"
 
+extern grpc_core::TraceFlag grpc_fd_trace;      /* Disabled by default */
 extern grpc_core::TraceFlag grpc_polling_trace; /* Disabled by default */
 
+#define GRPC_FD_TRACE(format, ...)                        \
+  if (grpc_fd_trace.enabled()) {                          \
+    gpr_log(GPR_INFO, "(fd-trace) " format, __VA_ARGS__); \
+  }
+
 typedef struct grpc_fd grpc_fd;
 
 typedef struct grpc_event_engine_vtable {
diff --git a/test/core/util/port_isolated_runtime_environment.cc b/test/core/util/port_isolated_runtime_environment.cc
index 5f0585e..ff8342f 100644
--- a/test/core/util/port_isolated_runtime_environment.cc
+++ b/test/core/util/port_isolated_runtime_environment.cc
@@ -19,19 +19,28 @@
 /* When running tests on remote machines, the framework takes a round-robin pick
  * of a port within certain range. There is no need to recycle ports.
  */
+#include <grpc/support/time.h>
+#include <stdlib.h>
 #include "src/core/lib/iomgr/port.h"
 #include "test/core/util/test_config.h"
 #if defined(GRPC_PORT_ISOLATED_RUNTIME)
 
 #include "test/core/util/port.h"
 
-#define LOWER_PORT 49152
-static int s_allocated_port = LOWER_PORT;
+#define MIN_PORT 49152
+#define MAX_PORT 65536
+
+int get_random_starting_port() {
+  srand(gpr_now(GPR_CLOCK_REALTIME).tv_nsec);
+  return rand() % (MAX_PORT - MIN_PORT + 1) + MIN_PORT;
+}
+
+static int s_allocated_port = get_random_starting_port();
 
 int grpc_pick_unused_port_or_die(void) {
   int allocated_port = s_allocated_port++;
-  if (s_allocated_port == 65536) {
-    s_allocated_port = LOWER_PORT;
+  if (s_allocated_port == MAX_PORT) {
+    s_allocated_port = MIN_PORT;
   }
 
   return allocated_port;
diff --git a/test/distrib/csharp/DistribTest/DistribTest.project.json b/test/distrib/csharp/DistribTest/DistribTest.project.json
deleted file mode 100644
index 422545e..0000000
--- a/test/distrib/csharp/DistribTest/DistribTest.project.json
+++ /dev/null
@@ -1,11 +0,0 @@
-// This file exists only to prevent VS2015 from mistakenly picking up
-// project.json file when building .csproj project.
-// See https://github.com/Microsoft/msbuild/issues/394
-{
-  "frameworks": {
-    "net45": { }
-  },
-  "runtimes": {
-    "win": { }
-  }
-}
diff --git a/test/distrib/csharp/DistribTest/DistribTestDotNet.csproj b/test/distrib/csharp/DistribTest/DistribTestDotNet.csproj
new file mode 100644
index 0000000..f16a0f9
--- /dev/null
+++ b/test/distrib/csharp/DistribTest/DistribTestDotNet.csproj
@@ -0,0 +1,27 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+  <PropertyGroup>
+    <OutputType>Exe</OutputType>
+    <TargetFrameworks>netcoreapp1.0;net45</TargetFrameworks>
+    <GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute>
+    <GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute>
+    <GenerateAssemblyDescriptionAttribute>false</GenerateAssemblyDescriptionAttribute>
+    <GenerateAssemblyFileVersionAttribute>false</GenerateAssemblyFileVersionAttribute>
+    <GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute>
+    <GenerateAssemblyTitleAttribute>false</GenerateAssemblyTitleAttribute>
+    <GenerateAssemblyVersionAttribute>false</GenerateAssemblyVersionAttribute>
+  </PropertyGroup>
+
+  <ItemGroup>
+    <PackageReference Include="Grpc" Version="__GRPC_NUGET_VERSION__" />
+    <PackageReference Include="Grpc.Auth" Version="__GRPC_NUGET_VERSION__" />
+    <PackageReference Include="Grpc.Tools" Version="__GRPC_NUGET_VERSION__" />
+  </ItemGroup>
+  
+  <PropertyGroup Condition="'$(OS)' != 'Windows_NT'">
+    <!-- Workaround for https://github.com/dotnet/sdk/issues/335 -->
+    <FrameworkPathOverride Condition="Exists('/usr/lib/mono/4.5-api')">/usr/lib/mono/4.5-api</FrameworkPathOverride>
+    <FrameworkPathOverride Condition="Exists('/usr/local/lib/mono/4.5-api')">/usr/local/lib/mono/4.5-api</FrameworkPathOverride>
+    <FrameworkPathOverride Condition="Exists('/Library/Frameworks/Mono.framework/Versions/Current/lib/mono/4.5-api')">/Library/Frameworks/Mono.framework/Versions/Current/lib/mono/4.5-api</FrameworkPathOverride>
+  </PropertyGroup>
+</Project>
\ No newline at end of file
diff --git a/test/distrib/csharp/DistribTest/project.json b/test/distrib/csharp/DistribTest/project.json
deleted file mode 100644
index 09266e5..0000000
--- a/test/distrib/csharp/DistribTest/project.json
+++ /dev/null
@@ -1,22 +0,0 @@
-{
-  "buildOptions": {
-    "emitEntryPoint": true
-  },
-  "dependencies": {
-    "Grpc.Auth": "__GRPC_NUGET_VERSION__",
-    "Grpc.Core": "__GRPC_NUGET_VERSION__",
-    // Necessary for native deps to get copied correctly.
-    "Microsoft.NETCore.Platforms": "1.0.1"
-  },
-  "frameworks": {
-    "net45": { },
-    "netcoreapp1.0": {
-      "dependencies": {
-        "Microsoft.NETCore.App": {
-          "type": "platform",
-          "version": "1.0.0"
-        }
-      }
-    }
-  }
-}
diff --git a/test/distrib/csharp/run_distrib_test.sh b/test/distrib/csharp/run_distrib_test.sh
index b968727..eee24d0 100755
--- a/test/distrib/csharp/run_distrib_test.sh
+++ b/test/distrib/csharp/run_distrib_test.sh
@@ -21,11 +21,6 @@
 
 ./update_version.sh auto
 
-# With a recent-enough version of mono, the "nuget restore" command would
-# restore packages based on project.json files, but we want to restore packages
-# based on the net45 legacy "packages.config" file instead.
-rm DistribTest/*project.json
-
 nuget restore
 
 xbuild DistribTest.sln
diff --git a/test/distrib/csharp/run_distrib_test_dotnetcli.sh b/test/distrib/csharp/run_distrib_test_dotnetcli.sh
index 9e31945..86c2d52 100755
--- a/test/distrib/csharp/run_distrib_test_dotnetcli.sh
+++ b/test/distrib/csharp/run_distrib_test_dotnetcli.sh
@@ -25,19 +25,22 @@
 
 # TODO(jtattermusch): make sure we don't pollute the global nuget cache with
 # the nugets being tested.
-dotnet restore
+dotnet restore DistribTestDotNet.csproj
 
-dotnet build
-dotnet publish
+dotnet build DistribTestDotNet.csproj
+dotnet publish -f netcoreapp1.0 DistribTestDotNet.csproj
+dotnet publish -f net45 DistribTestDotNet.csproj
+
+ls -R bin
 
 # .NET 4.5 target after dotnet build
-mono bin/Debug/net45/*-x64/DistribTest.exe
+mono bin/Debug/net45/publish/DistribTestDotNet.exe
 
 # .NET 4.5 target after dotnet publish
-mono bin/Debug/net45/*-x64/publish/DistribTest.exe
+mono bin/Debug/net45/publish/DistribTestDotNet.exe
 
 # .NET Core target after dotnet build
-dotnet exec bin/Debug/netcoreapp1.0/DistribTest.dll
+dotnet exec bin/Debug/netcoreapp1.0/DistribTestDotNet.dll
 
 # .NET Core target after dotnet publish
-dotnet exec bin/Debug/netcoreapp1.0/publish/DistribTest.dll
+dotnet exec bin/Debug/netcoreapp1.0/publish/DistribTestDotNet.dll
diff --git a/test/distrib/csharp/update_version.sh b/test/distrib/csharp/update_version.sh
index 734ec21..9759cc5 100755
--- a/test/distrib/csharp/update_version.sh
+++ b/test/distrib/csharp/update_version.sh
@@ -28,4 +28,4 @@
 fi
 
 # Replaces version placeholder with value provided as first argument.
-sed -ibak "s/__GRPC_NUGET_VERSION__/${CSHARP_VERSION}/g" DistribTest/packages.config DistribTest/DistribTest.csproj DistribTest/project.json
+sed -ibak "s/__GRPC_NUGET_VERSION__/${CSHARP_VERSION}/g" DistribTest/packages.config DistribTest/DistribTest.csproj DistribTest/DistribTestDotNet.csproj
diff --git a/tools/dockerfile/distribtest/csharp_ubuntu1404_x64/Dockerfile b/tools/dockerfile/distribtest/csharp_ubuntu1404_x64/Dockerfile
index 4bd3763..10279c5 100644
--- a/tools/dockerfile/distribtest/csharp_ubuntu1404_x64/Dockerfile
+++ b/tools/dockerfile/distribtest/csharp_ubuntu1404_x64/Dockerfile
@@ -22,16 +22,13 @@
     ca-certificates-mono \
     nuget
 
-# make sure we have nuget 2.12+ (in case there's an older cached docker image)
-RUN apt-get update && apt-get install -y nuget
-
 RUN apt-get update && apt-get install -y unzip
 
 # Install dotnet CLI
 RUN apt-get install -y apt-transport-https
 RUN sh -c 'echo "deb [arch=amd64] https://apt-mo.trafficmanager.net/repos/dotnet-release/ trusty main" > /etc/apt/sources.list.d/dotnetdev.list'
 RUN apt-key adv --keyserver apt-mo.trafficmanager.net --recv-keys 417A0893
-RUN apt-get update && apt-get install -y dotnet-dev-1.0.0-preview2-003121
+RUN apt-get update && apt-get install -y dotnet-dev-1.0.4
 
 # Trigger the population of the local package cache for dotnet CLI
 RUN mkdir warmup \
diff --git a/tools/doxygen/Doxyfile.c++ b/tools/doxygen/Doxyfile.c++
index 884eabb..450ff95 100644
--- a/tools/doxygen/Doxyfile.c++
+++ b/tools/doxygen/Doxyfile.c++
@@ -777,6 +777,7 @@
 doc/environment_variables.md \
 doc/epoll-polling-engine.md \
 doc/fail_fast.md \
+doc/fork_support.md \
 doc/g_stands_for.md \
 doc/health-checking.md \
 doc/http-grpc-status-mapping.md \
diff --git a/tools/doxygen/Doxyfile.c++.internal b/tools/doxygen/Doxyfile.c++.internal
index 66796ba..7c6b75d 100644
--- a/tools/doxygen/Doxyfile.c++.internal
+++ b/tools/doxygen/Doxyfile.c++.internal
@@ -777,6 +777,7 @@
 doc/environment_variables.md \
 doc/epoll-polling-engine.md \
 doc/fail_fast.md \
+doc/fork_support.md \
 doc/g_stands_for.md \
 doc/health-checking.md \
 doc/http-grpc-status-mapping.md \
diff --git a/tools/doxygen/Doxyfile.core b/tools/doxygen/Doxyfile.core
index 04f9d78..c47d36c 100644
--- a/tools/doxygen/Doxyfile.core
+++ b/tools/doxygen/Doxyfile.core
@@ -779,6 +779,7 @@
 doc/environment_variables.md \
 doc/epoll-polling-engine.md \
 doc/fail_fast.md \
+doc/fork_support.md \
 doc/g_stands_for.md \
 doc/health-checking.md \
 doc/http-grpc-status-mapping.md \
diff --git a/tools/doxygen/Doxyfile.core.internal b/tools/doxygen/Doxyfile.core.internal
index 0969b9c..2d15412 100644
--- a/tools/doxygen/Doxyfile.core.internal
+++ b/tools/doxygen/Doxyfile.core.internal
@@ -779,6 +779,7 @@
 doc/environment_variables.md \
 doc/epoll-polling-engine.md \
 doc/fail_fast.md \
+doc/fork_support.md \
 doc/g_stands_for.md \
 doc/health-checking.md \
 doc/http-grpc-status-mapping.md \
diff --git a/tools/run_tests/artifacts/build_artifact_csharp.bat b/tools/run_tests/artifacts/build_artifact_csharp.bat
index 91fdb0d..a84bc54 100644
--- a/tools/run_tests/artifacts/build_artifact_csharp.bat
+++ b/tools/run_tests/artifacts/build_artifact_csharp.bat
@@ -15,7 +15,7 @@
 @rem Builds C# artifacts on Windows
 
 set ARCHITECTURE=%1
-
+set GRPC_SKIP_DOTNET_RESTORE=true
 @call tools\run_tests\helper_scripts\pre_build_csharp.bat %ARCHITECTURE% || goto :error
 
 cd cmake\build\%ARCHITECTURE%
diff --git a/tools/run_tests/artifacts/build_artifact_protoc.bat b/tools/run_tests/artifacts/build_artifact_protoc.bat
index 2e9aae8..1d5bf03 100644
--- a/tools/run_tests/artifacts/build_artifact_protoc.bat
+++ b/tools/run_tests/artifacts/build_artifact_protoc.bat
@@ -22,11 +22,7 @@
 mkdir build
 cd build
 
-@rem TODO(jtattermusch): Stop hardcoding path to yasm once Jenkins workers can locate yasm correctly
-@rem If yasm is not on the path, use hardcoded path instead.
-yasm --version || set USE_HARDCODED_YASM_PATH_MAYBE=-DCMAKE_ASM_NASM_COMPILER="C:/Program Files (x86)/yasm/yasm.exe"
-
-cmake -G "%generator%" -DgRPC_BUILD_TESTS=OFF -DgRPC_MSVC_STATIC_RUNTIME=ON %USE_HARDCODED_YASM_PATH_MAYBE% ../.. || goto :error
+cmake -G "%generator%" -DgRPC_BUILD_TESTS=OFF -DgRPC_MSVC_STATIC_RUNTIME=ON ../.. || goto :error
 cmake --build . --target protoc --config Release || goto :error
 cmake --build . --target plugins --config Release || goto :error
 cd ..\..
diff --git a/tools/run_tests/helper_scripts/pre_build_cmake.bat b/tools/run_tests/helper_scripts/pre_build_cmake.bat
index d89fc5f..f077776 100644
--- a/tools/run_tests/helper_scripts/pre_build_cmake.bat
+++ b/tools/run_tests/helper_scripts/pre_build_cmake.bat
@@ -24,11 +24,7 @@
 mkdir build
 cd build
 
-@rem TODO(jtattermusch): Stop hardcoding path to yasm once Jenkins workers can locate yasm correctly
-@rem If yasm is not on the path, use hardcoded path instead.
-yasm --version || set USE_HARDCODED_YASM_PATH_MAYBE=-DCMAKE_ASM_NASM_COMPILER="C:/Program Files (x86)/yasm/yasm.exe"
-
-cmake -G %GENERATOR% -A %ARCHITECTURE% -DgRPC_BUILD_TESTS=ON %USE_HARDCODED_YASM_PATH_MAYBE% ../.. || goto :error
+cmake -G %GENERATOR% -A %ARCHITECTURE% -DgRPC_BUILD_TESTS=ON ../.. || goto :error
 
 endlocal
 
diff --git a/tools/run_tests/helper_scripts/pre_build_csharp.bat b/tools/run_tests/helper_scripts/pre_build_csharp.bat
index 63f6645..2ae870e 100644
--- a/tools/run_tests/helper_scripts/pre_build_csharp.bat
+++ b/tools/run_tests/helper_scripts/pre_build_csharp.bat
@@ -28,15 +28,13 @@
 mkdir %ARCHITECTURE%
 cd %ARCHITECTURE%
 
-@rem TODO(jtattermusch): Stop hardcoding path to yasm once Jenkins workers can locate yasm correctly
-@rem If yasm is not on the path, use hardcoded path instead.
-yasm --version || set USE_HARDCODED_YASM_PATH_MAYBE=-DCMAKE_ASM_NASM_COMPILER="C:/Program Files (x86)/yasm/yasm.exe"
-
-cmake -G "Visual Studio 14 2015" -A %ARCHITECTURE% -DgRPC_BUILD_TESTS=OFF -DgRPC_MSVC_STATIC_RUNTIME=ON %USE_HARDCODED_YASM_PATH_MAYBE% ../../.. || goto :error
+cmake -G "Visual Studio 14 2015" -A %ARCHITECTURE% -DgRPC_BUILD_TESTS=OFF -DgRPC_MSVC_STATIC_RUNTIME=ON ../../.. || goto :error
 
 cd ..\..\..\src\csharp
 
-dotnet restore Grpc.sln || goto :error
+if NOT DEFINED GRPC_SKIP_DOTNET_RESTORE (
+  dotnet restore Grpc.sln || goto :error
+)
 
 endlocal
 
diff --git a/tools/run_tests/python_utils/jobset.py b/tools/run_tests/python_utils/jobset.py
index 6a33913..561f453 100755
--- a/tools/run_tests/python_utils/jobset.py
+++ b/tools/run_tests/python_utils/jobset.py
@@ -31,7 +31,9 @@
 measure_cpu_costs = False
 
 _DEFAULT_MAX_JOBS = 16 * multiprocessing.cpu_count()
-_MAX_RESULT_SIZE = 8192
+# Maximum number of bytes of job's stdout that will be stored in the result.
+# Only last N bytes of stdout will be kept if the actual output longer.
+_MAX_RESULT_SIZE = 64 * 1024
 
 
 # NOTE: If you change this, please make sure to test reviewing the