Beispiel #1
0
/* Cleaning up a list with pending timers. */
void destruction_test(void) {
  grpc_timer timers[5];
  grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;

  grpc_timer_list_init(gpr_time_0(GPR_CLOCK_REALTIME));
  memset(cb_called, 0, sizeof(cb_called));

  grpc_timer_init(&exec_ctx, &timers[0], tfm(100), cb, (void *)(intptr_t)0,
                  gpr_time_0(GPR_CLOCK_REALTIME));
  grpc_timer_init(&exec_ctx, &timers[1], tfm(3), cb, (void *)(intptr_t)1,
                  gpr_time_0(GPR_CLOCK_REALTIME));
  grpc_timer_init(&exec_ctx, &timers[2], tfm(100), cb, (void *)(intptr_t)2,
                  gpr_time_0(GPR_CLOCK_REALTIME));
  grpc_timer_init(&exec_ctx, &timers[3], tfm(3), cb, (void *)(intptr_t)3,
                  gpr_time_0(GPR_CLOCK_REALTIME));
  grpc_timer_init(&exec_ctx, &timers[4], tfm(1), cb, (void *)(intptr_t)4,
                  gpr_time_0(GPR_CLOCK_REALTIME));
  GPR_ASSERT(1 == grpc_timer_check(&exec_ctx, tfm(2), NULL));
  grpc_exec_ctx_finish(&exec_ctx);
  GPR_ASSERT(1 == cb_called[4][1]);
  grpc_timer_cancel(&exec_ctx, &timers[0]);
  grpc_timer_cancel(&exec_ctx, &timers[3]);
  grpc_exec_ctx_finish(&exec_ctx);
  GPR_ASSERT(1 == cb_called[0][0]);
  GPR_ASSERT(1 == cb_called[3][0]);

  grpc_timer_list_shutdown(&exec_ctx);
  grpc_exec_ctx_finish(&exec_ctx);
  GPR_ASSERT(1 == cb_called[1][0]);
  GPR_ASSERT(1 == cb_called[2][0]);
}
// interleave waiting for an event with a timer check
static bool wait_loop(int deadline_seconds, gpr_event *ev) {
  while (deadline_seconds) {
    gpr_log(GPR_DEBUG, "Test: waiting for %d more seconds", deadline_seconds);
    if (gpr_event_wait(ev, grpc_timeout_seconds_to_deadline(1))) return true;
    deadline_seconds--;

    grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
    grpc_timer_check(&exec_ctx, gpr_now(GPR_CLOCK_MONOTONIC), NULL);
    grpc_exec_ctx_finish(&exec_ctx);
  }
  return false;
}
Beispiel #3
0
void test_fails(void) {
  grpc_resolved_address resolved_addr;
  struct sockaddr_in *addr = (struct sockaddr_in *)resolved_addr.addr;
  int connections_complete_before;
  grpc_closure done;
  grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;

  gpr_log(GPR_DEBUG, "test_fails");

  memset(&resolved_addr, 0, sizeof(resolved_addr));
  resolved_addr.len = sizeof(struct sockaddr_in);
  addr->sin_family = AF_INET;

  gpr_mu_lock(g_mu);
  connections_complete_before = g_connections_complete;
  gpr_mu_unlock(g_mu);

  /* connect to a broken address */
  GRPC_CLOSURE_INIT(&done, must_fail, NULL, grpc_schedule_on_exec_ctx);
  grpc_tcp_client_connect(&exec_ctx, &done, &g_connecting, NULL, NULL,
                          &resolved_addr, gpr_inf_future(GPR_CLOCK_REALTIME));

  gpr_mu_lock(g_mu);

  /* wait for the connection callback to finish */
  while (g_connections_complete == connections_complete_before) {
    grpc_pollset_worker *worker = NULL;
    gpr_timespec now = gpr_now(GPR_CLOCK_MONOTONIC);
    gpr_timespec polling_deadline = test_deadline();
    switch (grpc_timer_check(&exec_ctx, now, &polling_deadline)) {
      case GRPC_TIMERS_FIRED:
        break;
      case GRPC_TIMERS_NOT_CHECKED:
        polling_deadline = now;
      /* fall through */
      case GRPC_TIMERS_CHECKED_AND_EMPTY:
        GPR_ASSERT(GRPC_LOG_IF_ERROR(
            "pollset_work", grpc_pollset_work(&exec_ctx, g_pollset, &worker,
                                              now, polling_deadline)));
        break;
    }
    gpr_mu_unlock(g_mu);
    grpc_exec_ctx_flush(&exec_ctx);
    gpr_mu_lock(g_mu);
  }

  gpr_mu_unlock(g_mu);
  grpc_exec_ctx_finish(&exec_ctx);
}
void test_fails(void) {
  struct sockaddr_in addr;
  socklen_t addr_len = sizeof(addr);
  int connections_complete_before;
  grpc_closure done;
  grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;

  gpr_log(GPR_DEBUG, "test_fails");

  memset(&addr, 0, sizeof(addr));
  addr.sin_family = AF_INET;

  gpr_mu_lock(GRPC_POLLSET_MU(&g_pollset));
  connections_complete_before = g_connections_complete;
  gpr_mu_unlock(GRPC_POLLSET_MU(&g_pollset));

  /* connect to a broken address */
  grpc_closure_init(&done, must_fail, NULL);
  grpc_tcp_client_connect(&exec_ctx, &done, &g_connecting, &g_pollset_set,
                          (struct sockaddr *)&addr, addr_len,
                          gpr_inf_future(GPR_CLOCK_REALTIME));

  gpr_mu_lock(GRPC_POLLSET_MU(&g_pollset));

  /* wait for the connection callback to finish */
  while (g_connections_complete == connections_complete_before) {
    grpc_pollset_worker *worker = NULL;
    gpr_timespec now = gpr_now(GPR_CLOCK_MONOTONIC);
    gpr_timespec polling_deadline = test_deadline();
    if (!grpc_timer_check(&exec_ctx, now, &polling_deadline)) {
      grpc_pollset_work(&exec_ctx, &g_pollset, &worker, now, polling_deadline);
    }
    gpr_mu_unlock(GRPC_POLLSET_MU(&g_pollset));
    grpc_exec_ctx_flush(&exec_ctx);
    gpr_mu_lock(GRPC_POLLSET_MU(&g_pollset));
  }

  gpr_mu_unlock(GRPC_POLLSET_MU(&g_pollset));
  grpc_exec_ctx_finish(&exec_ctx);
}
Beispiel #5
0
static void add_test(void) {
  gpr_timespec start = gpr_now(GPR_CLOCK_REALTIME);
  int i;
  grpc_timer timers[20];
  grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;

  grpc_timer_list_init(start);
  memset(cb_called, 0, sizeof(cb_called));

  /* 10 ms timers.  will expire in the current epoch */
  for (i = 0; i < 10; i++) {
    grpc_timer_init(&exec_ctx, &timers[i],
                    gpr_time_add(start, gpr_time_from_millis(10, GPR_TIMESPAN)),
                    cb, (void *)(intptr_t)i, start);
  }

  /* 1010 ms timers.  will expire in the next epoch */
  for (i = 10; i < 20; i++) {
    grpc_timer_init(
        &exec_ctx, &timers[i],
        gpr_time_add(start, gpr_time_from_millis(1010, GPR_TIMESPAN)), cb,
        (void *)(intptr_t)i, start);
  }

  /* collect timers.  Only the first batch should be ready. */
  GPR_ASSERT(10 == grpc_timer_check(&exec_ctx,
                                    gpr_time_add(start, gpr_time_from_millis(
                                                            500, GPR_TIMESPAN)),
                                    NULL));
  grpc_exec_ctx_finish(&exec_ctx);
  for (i = 0; i < 20; i++) {
    GPR_ASSERT(cb_called[i][1] == (i < 10));
    GPR_ASSERT(cb_called[i][0] == 0);
  }

  GPR_ASSERT(0 == grpc_timer_check(&exec_ctx,
                                   gpr_time_add(start, gpr_time_from_millis(
                                                           600, GPR_TIMESPAN)),
                                   NULL));
  grpc_exec_ctx_finish(&exec_ctx);
  for (i = 0; i < 30; i++) {
    GPR_ASSERT(cb_called[i][1] == (i < 10));
    GPR_ASSERT(cb_called[i][0] == 0);
  }

  /* collect the rest of the timers */
  GPR_ASSERT(10 == grpc_timer_check(
                       &exec_ctx, gpr_time_add(start, gpr_time_from_millis(
                                                          1500, GPR_TIMESPAN)),
                       NULL));
  grpc_exec_ctx_finish(&exec_ctx);
  for (i = 0; i < 30; i++) {
    GPR_ASSERT(cb_called[i][1] == (i < 20));
    GPR_ASSERT(cb_called[i][0] == 0);
  }

  GPR_ASSERT(0 == grpc_timer_check(&exec_ctx,
                                   gpr_time_add(start, gpr_time_from_millis(
                                                           1600, GPR_TIMESPAN)),
                                   NULL));
  for (i = 0; i < 30; i++) {
    GPR_ASSERT(cb_called[i][1] == (i < 20));
    GPR_ASSERT(cb_called[i][0] == 0);
  }

  grpc_timer_list_shutdown(&exec_ctx);
  grpc_exec_ctx_finish(&exec_ctx);
}
Beispiel #6
0
void grpc_pollset_work(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset,
                       grpc_pollset_worker *worker, gpr_timespec now,
                       gpr_timespec deadline) {
  /* pollset->mu already held */
  int added_worker = 0;
  int locked = 1;
  int queued_work = 0;
  int keep_polling = 0;
  GPR_TIMER_BEGIN("grpc_pollset_work", 0);
  /* this must happen before we (potentially) drop pollset->mu */
  worker->next = worker->prev = NULL;
  worker->reevaluate_polling_on_wakeup = 0;
  if (pollset->local_wakeup_cache != NULL) {
    worker->wakeup_fd = pollset->local_wakeup_cache;
    pollset->local_wakeup_cache = worker->wakeup_fd->next;
  } else {
    worker->wakeup_fd = gpr_malloc(sizeof(*worker->wakeup_fd));
    grpc_wakeup_fd_init(&worker->wakeup_fd->fd);
  }
  worker->kicked_specifically = 0;
  /* If there's work waiting for the pollset to be idle, and the
     pollset is idle, then do that work */
  if (!grpc_pollset_has_workers(pollset) &&
      !grpc_closure_list_empty(pollset->idle_jobs)) {
    GPR_TIMER_MARK("grpc_pollset_work.idle_jobs", 0);
    grpc_exec_ctx_enqueue_list(exec_ctx, &pollset->idle_jobs);
    goto done;
  }
  /* Check alarms - these are a global resource so we just ping
     each time through on every pollset.
     May update deadline to ensure timely wakeups.
     TODO(ctiller): can this work be localized? */
  if (grpc_timer_check(exec_ctx, now, &deadline)) {
    GPR_TIMER_MARK("grpc_pollset_work.alarm_triggered", 0);
    gpr_mu_unlock(&pollset->mu);
    locked = 0;
    goto done;
  }
  /* If we're shutting down then we don't execute any extended work */
  if (pollset->shutting_down) {
    GPR_TIMER_MARK("grpc_pollset_work.shutting_down", 0);
    goto done;
  }
  /* Give do_promote priority so we don't starve it out */
  if (pollset->in_flight_cbs) {
    GPR_TIMER_MARK("grpc_pollset_work.in_flight_cbs", 0);
    gpr_mu_unlock(&pollset->mu);
    locked = 0;
    goto done;
  }
  /* Start polling, and keep doing so while we're being asked to
     re-evaluate our pollers (this allows poll() based pollers to
     ensure they don't miss wakeups) */
  keep_polling = 1;
  while (keep_polling) {
    keep_polling = 0;
    if (!pollset->kicked_without_pollers) {
      if (!added_worker) {
        push_front_worker(pollset, worker);
        added_worker = 1;
        gpr_tls_set(&g_current_thread_worker, (gpr_intptr)worker);
      }
      gpr_tls_set(&g_current_thread_poller, (gpr_intptr)pollset);
      GPR_TIMER_BEGIN("maybe_work_and_unlock", 0);
      pollset->vtable->maybe_work_and_unlock(exec_ctx, pollset, worker,
                                             deadline, now);
      GPR_TIMER_END("maybe_work_and_unlock", 0);
      locked = 0;
      gpr_tls_set(&g_current_thread_poller, 0);
    } else {
      GPR_TIMER_MARK("grpc_pollset_work.kicked_without_pollers", 0);
      pollset->kicked_without_pollers = 0;
    }
  /* Finished execution - start cleaning up.
     Note that we may arrive here from outside the enclosing while() loop.
     In that case we won't loop though as we haven't added worker to the
     worker list, which means nobody could ask us to re-evaluate polling). */
  done:
    if (!locked) {
      queued_work |= grpc_exec_ctx_flush(exec_ctx);
      gpr_mu_lock(&pollset->mu);
      locked = 1;
    }
    /* If we're forced to re-evaluate polling (via grpc_pollset_kick with
       GRPC_POLLSET_REEVALUATE_POLLING_ON_WAKEUP) then we land here and force
       a loop */
    if (worker->reevaluate_polling_on_wakeup) {
      worker->reevaluate_polling_on_wakeup = 0;
      pollset->kicked_without_pollers = 0;
      if (queued_work || worker->kicked_specifically) {
        /* If there's queued work on the list, then set the deadline to be
           immediate so we get back out of the polling loop quickly */
        deadline = gpr_inf_past(GPR_CLOCK_MONOTONIC);
      }
      keep_polling = 1;
    }
  }
  if (added_worker) {
    remove_worker(pollset, worker);
    gpr_tls_set(&g_current_thread_worker, 0);
  }
  /* release wakeup fd to the local pool */
  worker->wakeup_fd->next = pollset->local_wakeup_cache;
  pollset->local_wakeup_cache = worker->wakeup_fd;
  /* check shutdown conditions */
  if (pollset->shutting_down) {
    if (grpc_pollset_has_workers(pollset)) {
      grpc_pollset_kick(pollset, NULL);
    } else if (!pollset->called_shutdown && pollset->in_flight_cbs == 0) {
      pollset->called_shutdown = 1;
      gpr_mu_unlock(&pollset->mu);
      finish_shutdown(exec_ctx, pollset);
      grpc_exec_ctx_flush(exec_ctx);
      /* 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);
    } else if (!grpc_closure_list_empty(pollset->idle_jobs)) {
      grpc_exec_ctx_enqueue_list(exec_ctx, &pollset->idle_jobs);
      gpr_mu_unlock(&pollset->mu);
      grpc_exec_ctx_flush(exec_ctx);
      gpr_mu_lock(&pollset->mu);
    }
  }
  GPR_TIMER_END("grpc_pollset_work", 0);
}
Beispiel #7
0
void grpc_pollset_work(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset,
                       grpc_pollset_worker *worker, gpr_timespec now,
                       gpr_timespec deadline) {
  int added_worker = 0;
  worker->links[GRPC_POLLSET_WORKER_LINK_POLLSET].next =
      worker->links[GRPC_POLLSET_WORKER_LINK_POLLSET].prev =
          worker->links[GRPC_POLLSET_WORKER_LINK_GLOBAL].next =
              worker->links[GRPC_POLLSET_WORKER_LINK_GLOBAL].prev = NULL;
  worker->kicked = 0;
  worker->pollset = pollset;
  gpr_cv_init(&worker->cv);
  if (grpc_timer_check(exec_ctx, now, &deadline)) {
    goto done;
  }
  if (!pollset->kicked_without_pollers && !pollset->shutting_down) {
    if (g_active_poller == NULL) {
      grpc_pollset_worker *next_worker;
      /* become poller */
      pollset->is_iocp_worker = 1;
      g_active_poller = worker;
      gpr_mu_unlock(&grpc_polling_mu);
      grpc_iocp_work(exec_ctx, deadline);
      grpc_exec_ctx_flush(exec_ctx);
      gpr_mu_lock(&grpc_polling_mu);
      pollset->is_iocp_worker = 0;
      g_active_poller = NULL;
      /* try to get a worker from this pollsets worker list */
      next_worker = pop_front_worker(&pollset->root_worker,
                                     GRPC_POLLSET_WORKER_LINK_POLLSET);
      if (next_worker == NULL) {
        /* try to get a worker from the global list */
        next_worker = pop_front_worker(&g_global_root_worker,
                                       GRPC_POLLSET_WORKER_LINK_GLOBAL);
      }
      if (next_worker != NULL) {
        next_worker->kicked = 1;
        gpr_cv_signal(&next_worker->cv);
      }

      if (pollset->shutting_down && pollset->on_shutdown != NULL) {
        grpc_exec_ctx_enqueue(exec_ctx, pollset->on_shutdown, 1);
        pollset->on_shutdown = NULL;
      }
      goto done;
    }
    push_front_worker(&g_global_root_worker, GRPC_POLLSET_WORKER_LINK_GLOBAL,
                      worker);
    push_front_worker(&pollset->root_worker, GRPC_POLLSET_WORKER_LINK_POLLSET,
                      worker);
    added_worker = 1;
    while (!worker->kicked) {
      if (gpr_cv_wait(&worker->cv, &grpc_polling_mu, deadline)) {
        break;
      }
    }
  } else {
    pollset->kicked_without_pollers = 0;
  }
done:
  if (!grpc_closure_list_empty(exec_ctx->closure_list)) {
    gpr_mu_unlock(&grpc_polling_mu);
    grpc_exec_ctx_flush(exec_ctx);
    gpr_mu_lock(&grpc_polling_mu);
  }
  if (added_worker) {
    remove_worker(worker, GRPC_POLLSET_WORKER_LINK_GLOBAL);
    remove_worker(worker, GRPC_POLLSET_WORKER_LINK_POLLSET);
  }
  gpr_cv_destroy(&worker->cv);
}
void test_times_out(void) {
  struct sockaddr_in addr;
  socklen_t addr_len = sizeof(addr);
  int svr_fd;
#define NUM_CLIENT_CONNECTS 100
  int client_fd[NUM_CLIENT_CONNECTS];
  int i;
  int r;
  int connections_complete_before;
  gpr_timespec connect_deadline;
  grpc_closure done;
  grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;

  gpr_log(GPR_DEBUG, "test_times_out");

  memset(&addr, 0, sizeof(addr));
  addr.sin_family = AF_INET;

  /* create a dummy server */
  svr_fd = socket(AF_INET, SOCK_STREAM, 0);
  GPR_ASSERT(svr_fd >= 0);
  GPR_ASSERT(0 == bind(svr_fd, (struct sockaddr *)&addr, addr_len));
  GPR_ASSERT(0 == listen(svr_fd, 1));
  /* Get its address */
  GPR_ASSERT(getsockname(svr_fd, (struct sockaddr *)&addr, &addr_len) == 0);

  /* tie up the listen buffer, which is somewhat arbitrarily sized. */
  for (i = 0; i < NUM_CLIENT_CONNECTS; ++i) {
    client_fd[i] = socket(AF_INET, SOCK_STREAM, 0);
    grpc_set_socket_nonblocking(client_fd[i], 1);
    do {
      r = connect(client_fd[i], (struct sockaddr *)&addr, addr_len);
    } while (r == -1 && errno == EINTR);
    GPR_ASSERT(r < 0);
    GPR_ASSERT(errno == EWOULDBLOCK || errno == EINPROGRESS);
  }

  /* connect to dummy server address */

  connect_deadline = GRPC_TIMEOUT_SECONDS_TO_DEADLINE(1);

  gpr_mu_lock(GRPC_POLLSET_MU(&g_pollset));
  connections_complete_before = g_connections_complete;
  gpr_mu_unlock(GRPC_POLLSET_MU(&g_pollset));

  grpc_closure_init(&done, must_fail, NULL);
  grpc_tcp_client_connect(&exec_ctx, &done, &g_connecting, &g_pollset_set,
                          (struct sockaddr *)&addr, addr_len, connect_deadline);

  /* Make sure the event doesn't trigger early */
  gpr_mu_lock(GRPC_POLLSET_MU(&g_pollset));
  for (;;) {
    grpc_pollset_worker *worker = NULL;
    gpr_timespec now = gpr_now(connect_deadline.clock_type);
    gpr_timespec continue_verifying_time =
        gpr_time_from_seconds(5, GPR_TIMESPAN);
    gpr_timespec grace_time = gpr_time_from_seconds(3, GPR_TIMESPAN);
    gpr_timespec finish_time =
        gpr_time_add(connect_deadline, continue_verifying_time);
    gpr_timespec restart_verifying_time =
        gpr_time_add(connect_deadline, grace_time);
    int is_after_deadline = gpr_time_cmp(now, connect_deadline) > 0;
    if (gpr_time_cmp(now, finish_time) > 0) {
      break;
    }
    gpr_log(GPR_DEBUG, "now=%lld.%09d connect_deadline=%lld.%09d",
            (long long)now.tv_sec, (int)now.tv_nsec,
            (long long)connect_deadline.tv_sec, (int)connect_deadline.tv_nsec);
    if (is_after_deadline && gpr_time_cmp(now, restart_verifying_time) <= 0) {
      /* allow some slack before insisting that things be done */
    } else {
      GPR_ASSERT(g_connections_complete ==
                 connections_complete_before + is_after_deadline);
    }
    gpr_timespec polling_deadline = GRPC_TIMEOUT_MILLIS_TO_DEADLINE(10);
    if (!grpc_timer_check(&exec_ctx, now, &polling_deadline)) {
      grpc_pollset_work(&exec_ctx, &g_pollset, &worker, now, polling_deadline);
    }
    gpr_mu_unlock(GRPC_POLLSET_MU(&g_pollset));
    grpc_exec_ctx_flush(&exec_ctx);
    gpr_mu_lock(GRPC_POLLSET_MU(&g_pollset));
  }
  gpr_mu_unlock(GRPC_POLLSET_MU(&g_pollset));

  grpc_exec_ctx_finish(&exec_ctx);

  close(svr_fd);
  for (i = 0; i < NUM_CLIENT_CONNECTS; ++i) {
    close(client_fd[i]);
  }
}
Beispiel #9
0
void grpc_iomgr_shutdown(void) {
  gpr_timespec shutdown_deadline = gpr_time_add(
      gpr_now(GPR_CLOCK_REALTIME), gpr_time_from_seconds(10, GPR_TIMESPAN));
  gpr_timespec last_warning_time = gpr_now(GPR_CLOCK_REALTIME);
  grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;

  grpc_iomgr_platform_flush();

  gpr_mu_lock(&g_mu);
  g_shutdown = 1;
  while (g_root_object.next != &g_root_object) {
    if (gpr_time_cmp(
            gpr_time_sub(gpr_now(GPR_CLOCK_REALTIME), last_warning_time),
            gpr_time_from_seconds(1, GPR_TIMESPAN)) >= 0) {
      if (g_root_object.next != &g_root_object) {
        gpr_log(GPR_DEBUG, "Waiting for %d iomgr objects to be destroyed",
                count_objects());
      }
      last_warning_time = gpr_now(GPR_CLOCK_REALTIME);
    }
    if (grpc_timer_check(&exec_ctx, gpr_inf_future(GPR_CLOCK_MONOTONIC),
                         NULL)) {
      gpr_mu_unlock(&g_mu);
      grpc_exec_ctx_flush(&exec_ctx);
      gpr_mu_lock(&g_mu);
      continue;
    }
    if (g_root_object.next != &g_root_object) {
      gpr_timespec short_deadline = gpr_time_add(
          gpr_now(GPR_CLOCK_REALTIME), gpr_time_from_millis(100, GPR_TIMESPAN));
      if (gpr_cv_wait(&g_rcv, &g_mu, short_deadline)) {
        if (gpr_time_cmp(gpr_now(GPR_CLOCK_REALTIME), shutdown_deadline) > 0) {
          if (g_root_object.next != &g_root_object) {
            gpr_log(GPR_DEBUG,
                    "Failed to free %d iomgr objects before shutdown deadline: "
                    "memory leaks are likely",
                    count_objects());
            dump_objects("LEAKED");
            if (grpc_iomgr_abort_on_leaks()) {
              abort();
            }
          }
          break;
        }
      }
    }
  }
  gpr_mu_unlock(&g_mu);

  grpc_timer_list_shutdown(&exec_ctx);
  grpc_exec_ctx_finish(&exec_ctx);

  /* ensure all threads have left g_mu */
  gpr_mu_lock(&g_mu);
  gpr_mu_unlock(&g_mu);

  grpc_pollset_global_shutdown();
  grpc_iomgr_platform_shutdown();
  grpc_exec_ctx_global_shutdown();
  gpr_mu_destroy(&g_mu);
  gpr_cv_destroy(&g_rcv);
}