Esempio n. 1
0
void grpc_pollset_reset(grpc_pollset *pollset) {
  GPR_ASSERT(pollset->shutting_down);
  GPR_ASSERT(pollset->in_flight_cbs == 0);
  GPR_ASSERT(!grpc_pollset_has_workers(pollset));
  GPR_ASSERT(pollset->idle_jobs.head == pollset->idle_jobs.tail);
  pollset->vtable->destroy(pollset);
  pollset->shutting_down = 0;
  pollset->called_shutdown = 0;
  pollset->kicked_without_pollers = 0;
  become_basic_pollset(pollset, NULL);
}
Esempio n. 2
0
void grpc_pollset_destroy(grpc_pollset *pollset) {
    GPR_ASSERT(pollset->in_flight_cbs == 0);
    GPR_ASSERT(!grpc_pollset_has_workers(pollset));
    GPR_ASSERT(pollset->idle_jobs.head == pollset->idle_jobs.tail);
    pollset->vtable->destroy(pollset);
    while (pollset->local_wakeup_cache) {
        grpc_cached_wakeup_fd *next = pollset->local_wakeup_cache->next;
        grpc_wakeup_fd_destroy(&pollset->local_wakeup_cache->fd);
        gpr_free(pollset->local_wakeup_cache);
        pollset->local_wakeup_cache = next;
    }
}
Esempio n. 3
0
void grpc_pollset_work(grpc_pollset *pollset, grpc_pollset_worker *worker,
                       gpr_timespec now, gpr_timespec deadline) {
  /* pollset->mu already held */
  int added_worker = 0;
  /* this must happen before we (potentially) drop pollset->mu */
  worker->next = worker->prev = NULL;
  /* TODO(ctiller): pool these */
  grpc_wakeup_fd_init(&worker->wakeup_fd);
  if (grpc_maybe_call_delayed_callbacks(&pollset->mu, 1)) {
    goto done;
  }
  if (grpc_alarm_check(&pollset->mu, now, &deadline)) {
    goto done;
  }
  if (pollset->shutting_down) {
    goto done;
  }
  if (pollset->in_flight_cbs) {
    /* Give do_promote priority so we don't starve it out */
    gpr_mu_unlock(&pollset->mu);
    gpr_mu_lock(&pollset->mu);
    goto done;
  }
  if (!pollset->kicked_without_pollers) {
    push_front_worker(pollset, worker);
    added_worker = 1;
    gpr_tls_set(&g_current_thread_poller, (gpr_intptr)pollset);
    pollset->vtable->maybe_work(pollset, worker, deadline, now, 1);
    gpr_tls_set(&g_current_thread_poller, 0);
  } else {
    pollset->kicked_without_pollers = 0;
  }
done:
  grpc_wakeup_fd_destroy(&worker->wakeup_fd);
  if (added_worker) {
    remove_worker(pollset, worker);
  }
  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(pollset);
      /* 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);
    }
  }
}
Esempio n. 4
0
void grpc_pollset_shutdown(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset,
                           grpc_closure *closure) {
  int call_shutdown = 0;
  gpr_mu_lock(&pollset->mu);
  GPR_ASSERT(!pollset->shutting_down);
  pollset->shutting_down = 1;
  if (!pollset->called_shutdown && pollset->in_flight_cbs == 0 &&
      !grpc_pollset_has_workers(pollset)) {
    pollset->called_shutdown = 1;
    call_shutdown = 1;
  }
  if (!grpc_pollset_has_workers(pollset)) {
    grpc_exec_ctx_enqueue_list(exec_ctx, &pollset->idle_jobs);
  }
  pollset->shutdown_done = closure;
  grpc_pollset_kick(pollset, GRPC_POLLSET_KICK_BROADCAST);
  gpr_mu_unlock(&pollset->mu);

  if (call_shutdown) {
    finish_shutdown(exec_ctx, pollset);
  }
}
Esempio n. 5
0
static void basic_pollset_add_fd(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset,
                                 grpc_fd *fd, int and_unlock_pollset) {
    grpc_unary_promote_args *up_args;
    GPR_ASSERT(fd);
    if (fd == pollset->data.ptr) goto exit;

    if (!grpc_pollset_has_workers(pollset)) {
        /* Fast path -- no in flight cbs */
        /* TODO(klempner): Comment this out and fix any test failures or establish
         * they are due to timing issues */
        grpc_fd *fds[2];
        fds[0] = pollset->data.ptr;
        fds[1] = fd;

        if (fds[0] == NULL) {
            pollset->data.ptr = fd;
            GRPC_FD_REF(fd, "basicpoll");
        } else if (!grpc_fd_is_orphaned(fds[0])) {
            grpc_platform_become_multipoller(exec_ctx, pollset, fds,
                                             GPR_ARRAY_SIZE(fds));
            GRPC_FD_UNREF(fds[0], "basicpoll");
        } else {
            /* old fd is orphaned and we haven't cleaned it up until now, so remain a
             * unary poller */
            GRPC_FD_UNREF(fds[0], "basicpoll");
            pollset->data.ptr = fd;
            GRPC_FD_REF(fd, "basicpoll");
        }
        goto exit;
    }

    /* Now we need to promote. This needs to happen when we're not polling. Since
     * this may be called from poll, the wait needs to happen asynchronously. */
    GRPC_FD_REF(fd, "basicpoll_add");
    pollset->in_flight_cbs++;
    up_args = gpr_malloc(sizeof(*up_args));
    up_args->fd = fd;
    up_args->original_vtable = pollset->vtable;
    up_args->pollset = pollset;
    up_args->promotion_closure.cb = basic_do_promote;
    up_args->promotion_closure.cb_arg = up_args;

    grpc_closure_list_add(&pollset->idle_jobs, &up_args->promotion_closure, 1);
    grpc_pollset_kick(pollset, GRPC_POLLSET_KICK_BROADCAST);

exit:
    if (and_unlock_pollset) {
        gpr_mu_unlock(&pollset->mu);
    }
}
Esempio n. 6
0
void grpc_pollset_shutdown(grpc_pollset *pollset,
                           void (*shutdown_done)(void *arg),
                           void *shutdown_done_arg) {
  int call_shutdown = 0;
  gpr_mu_lock(&pollset->mu);
  GPR_ASSERT(!pollset->shutting_down);
  pollset->shutting_down = 1;
  if (!pollset->called_shutdown && pollset->in_flight_cbs == 0 &&
      !grpc_pollset_has_workers(pollset)) {
    pollset->called_shutdown = 1;
    call_shutdown = 1;
  }
  pollset->shutdown_done_cb = shutdown_done;
  pollset->shutdown_done_arg = shutdown_done_arg;
  grpc_pollset_kick(pollset, GRPC_POLLSET_KICK_BROADCAST);
  gpr_mu_unlock(&pollset->mu);

  if (call_shutdown) {
    finish_shutdown(pollset);
  }
}
Esempio n. 7
0
static void basic_do_promote(grpc_exec_ctx *exec_ctx, void *args,
                             bool success) {
    grpc_unary_promote_args *up_args = args;
    const grpc_pollset_vtable *original_vtable = up_args->original_vtable;
    grpc_pollset *pollset = up_args->pollset;
    grpc_fd *fd = up_args->fd;

    /*
     * This is quite tricky. There are a number of cases to keep in mind here:
     * 1. fd may have been orphaned
     * 2. The pollset may no longer be a unary poller (and we can't let case #1
     * leak to other pollset types!)
     * 3. pollset's fd (which may have changed) may have been orphaned
     * 4. The pollset may be shutting down.
     */

    gpr_mu_lock(&pollset->mu);
    /* First we need to ensure that nobody is polling concurrently */
    GPR_ASSERT(!grpc_pollset_has_workers(pollset));

    gpr_free(up_args);
    /* At this point the pollset may no longer be a unary poller. In that case
     * we should just call the right add function and be done. */
    /* TODO(klempner): If we're not careful this could cause infinite recursion.
     * That's not a problem for now because empty_pollset has a trivial poller
     * and we don't have any mechanism to unbecome multipoller. */
    pollset->in_flight_cbs--;
    if (pollset->shutting_down) {
        /* We don't care about this pollset anymore. */
        if (pollset->in_flight_cbs == 0 && !pollset->called_shutdown) {
            pollset->called_shutdown = 1;
            finish_shutdown(exec_ctx, pollset);
        }
    } else if (grpc_fd_is_orphaned(fd)) {
        /* Don't try to add it to anything, we'll drop our ref on it below */
    } else if (pollset->vtable != original_vtable) {
        pollset->vtable->add_fd(exec_ctx, pollset, fd, 0);
    } else if (fd != pollset->data.ptr) {
        grpc_fd *fds[2];
        fds[0] = pollset->data.ptr;
        fds[1] = fd;

        if (fds[0] && !grpc_fd_is_orphaned(fds[0])) {
            grpc_platform_become_multipoller(exec_ctx, pollset, fds,
                                             GPR_ARRAY_SIZE(fds));
            GRPC_FD_UNREF(fds[0], "basicpoll");
        } else {
            /* old fd is orphaned and we haven't cleaned it up until now, so remain a
             * unary poller */
            /* Note that it is possible that fds[1] is also orphaned at this point.
             * That's okay, we'll correct it at the next add or poll. */
            if (fds[0]) GRPC_FD_UNREF(fds[0], "basicpoll");
            pollset->data.ptr = fd;
            GRPC_FD_REF(fd, "basicpoll");
        }
    }

    gpr_mu_unlock(&pollset->mu);

    /* Matching ref in basic_pollset_add_fd */
    GRPC_FD_UNREF(fd, "basicpoll_add");
}
Esempio n. 8
0
void grpc_pollset_work(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset,
                       grpc_pollset_worker **worker_hdl, gpr_timespec now,
                       gpr_timespec deadline) {
    grpc_pollset_worker worker;
    *worker_hdl = &worker;

    /* 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, NULL);
        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, (intptr_t)&worker);
            }
            gpr_tls_set(&g_current_thread_poller, (intptr_t)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, NULL);
            gpr_mu_unlock(&pollset->mu);
            grpc_exec_ctx_flush(exec_ctx);
            gpr_mu_lock(&pollset->mu);
        }
    }
    *worker_hdl = NULL;
    GPR_TIMER_END("grpc_pollset_work", 0);
}