void pf_shutdown(grpc_exec_ctx *exec_ctx, grpc_lb_policy *pol) { pick_first_lb_policy *p = (pick_first_lb_policy *)pol; pending_pick *pp; grpc_connected_subchannel *selected; gpr_mu_lock(&p->mu); selected = GET_SELECTED(p); p->shutdown = 1; pp = p->pending_picks; p->pending_picks = NULL; grpc_connectivity_state_set(exec_ctx, &p->state_tracker, GRPC_CHANNEL_FATAL_FAILURE, "shutdown"); /* cancel subscription */ if (selected != NULL) { grpc_connected_subchannel_notify_on_state_change( exec_ctx, selected, NULL, NULL, &p->connectivity_changed); } else { grpc_subchannel_notify_on_state_change( exec_ctx, p->subchannels[p->checking_subchannel], NULL, NULL, &p->connectivity_changed); } gpr_mu_unlock(&p->mu); while (pp != NULL) { pending_pick *next = pp->next; *pp->target = NULL; grpc_pollset_set_del_pollset(exec_ctx, &p->base.interested_parties, pp->pollset); grpc_exec_ctx_enqueue(exec_ctx, pp->on_complete, 1); gpr_free(pp); pp = next; } }
static void rr_cancel_picks(grpc_exec_ctx *exec_ctx, grpc_lb_policy *pol, uint32_t initial_metadata_flags_mask, uint32_t initial_metadata_flags_eq) { round_robin_lb_policy *p = (round_robin_lb_policy *)pol; pending_pick *pp; gpr_mu_lock(&p->mu); pp = p->pending_picks; p->pending_picks = NULL; while (pp != NULL) { pending_pick *next = pp->next; if ((pp->initial_metadata_flags & initial_metadata_flags_mask) == initial_metadata_flags_eq) { grpc_pollset_set_del_pollset(exec_ctx, p->base.interested_parties, pp->pollset); *pp->target = NULL; grpc_exec_ctx_enqueue(exec_ctx, pp->on_complete, false, NULL); gpr_free(pp); } else { pp->next = p->pending_picks; p->pending_picks = pp; } pp = next; } gpr_mu_unlock(&p->mu); }
/** implementation of del_interested_party for setup vtable */ static void setup_del_interested_party(grpc_transport_setup *sp, grpc_pollset *pollset) { grpc_client_setup *s = (grpc_client_setup *)sp; gpr_mu_lock(&s->mu); grpc_pollset_set_del_pollset(&s->interested_parties, pollset); gpr_mu_unlock(&s->mu); }
static void on_external_watch_complete(grpc_exec_ctx *exec_ctx, void *arg, grpc_error *error) { external_connectivity_watcher *w = arg; grpc_closure *follow_up = w->on_complete; grpc_pollset_set_del_pollset(exec_ctx, w->chand->interested_parties, w->pollset); GRPC_CHANNEL_STACK_UNREF(exec_ctx, w->chand->owning_stack, "external_connectivity_watcher"); gpr_free(w); follow_up->cb(exec_ctx, follow_up->cb_arg, error); }
void args_finish(grpc_exec_ctx *exec_ctx, args_struct *args) { GPR_ASSERT(gpr_event_wait(&args->ev, test_deadline())); grpc_resolved_addresses_destroy(args->addrs); grpc_pollset_set_del_pollset(exec_ctx, args->pollset_set, args->pollset); grpc_pollset_set_destroy(exec_ctx, args->pollset_set); grpc_closure do_nothing_cb; GRPC_CLOSURE_INIT(&do_nothing_cb, do_nothing, NULL, grpc_schedule_on_exec_ctx); grpc_pollset_shutdown(exec_ctx, args->pollset, &do_nothing_cb); // exec_ctx needs to be flushed before calling grpc_pollset_destroy() grpc_exec_ctx_flush(exec_ctx); grpc_pollset_destroy(exec_ctx, args->pollset); gpr_free(args->pollset); }
void grpc_polling_entity_del_from_pollset_set(grpc_exec_ctx *exec_ctx, grpc_polling_entity *pollent, grpc_pollset_set *pss_dst) { if (pollent->tag == POPS_POLLSET) { GPR_ASSERT(pollent->pollent.pollset != NULL); grpc_pollset_set_del_pollset(exec_ctx, pss_dst, pollent->pollent.pollset); } else if (pollent->tag == POPS_POLLSET_SET) { GPR_ASSERT(pollent->pollent.pollset_set != NULL); grpc_pollset_set_del_pollset_set(exec_ctx, pss_dst, pollent->pollent.pollset_set); } else { gpr_log(GPR_ERROR, "Invalid grpc_polling_entity tag '%d'", pollent->tag); abort(); } }
static void finish(internal_request *req, int success) { grpc_pollset_set_del_pollset(&req->context->pollset_set, req->pollset); req->on_response(req->user_data, success ? &req->parser.r : NULL); grpc_httpcli_parser_destroy(&req->parser); if (req->addresses != NULL) { grpc_resolved_addresses_destroy(req->addresses); } if (req->ep != NULL) { grpc_endpoint_destroy(req->ep); } gpr_slice_unref(req->request_text); gpr_free(req->host); grpc_iomgr_unregister_object(&req->iomgr_obj); gpr_free(req); }
static void pf_cancel_pick(grpc_exec_ctx *exec_ctx, grpc_lb_policy *pol, grpc_connected_subchannel **target) { pick_first_lb_policy *p = (pick_first_lb_policy *)pol; pending_pick *pp; gpr_mu_lock(&p->mu); pp = p->pending_picks; p->pending_picks = NULL; while (pp != NULL) { pending_pick *next = pp->next; if (pp->target == target) { grpc_pollset_set_del_pollset(exec_ctx, &p->base.interested_parties, pp->pollset); *target = NULL; grpc_exec_ctx_enqueue(exec_ctx, pp->on_complete, 0); gpr_free(pp); } else { pp->next = p->pending_picks; p->pending_picks = pp; } pp = next; } gpr_mu_unlock(&p->mu); }
static void pf_connectivity_changed(grpc_exec_ctx *exec_ctx, void *arg, int iomgr_success) { pick_first_lb_policy *p = arg; grpc_subchannel *selected_subchannel; pending_pick *pp; grpc_connected_subchannel *selected; gpr_mu_lock(&p->mu); selected = GET_SELECTED(p); if (p->shutdown) { gpr_mu_unlock(&p->mu); GRPC_LB_POLICY_WEAK_UNREF(exec_ctx, &p->base, "pick_first_connectivity"); return; } else if (selected != NULL) { if (p->checking_connectivity == GRPC_CHANNEL_TRANSIENT_FAILURE) { /* if the selected channel goes bad, we're done */ p->checking_connectivity = GRPC_CHANNEL_FATAL_FAILURE; } grpc_connectivity_state_set(exec_ctx, &p->state_tracker, p->checking_connectivity, "selected_changed"); if (p->checking_connectivity != GRPC_CHANNEL_FATAL_FAILURE) { grpc_connected_subchannel_notify_on_state_change( exec_ctx, selected, &p->base.interested_parties, &p->checking_connectivity, &p->connectivity_changed); } else { GRPC_LB_POLICY_WEAK_UNREF(exec_ctx, &p->base, "pick_first_connectivity"); } } else { loop: switch (p->checking_connectivity) { case GRPC_CHANNEL_READY: grpc_connectivity_state_set(exec_ctx, &p->state_tracker, GRPC_CHANNEL_READY, "connecting_ready"); selected_subchannel = p->subchannels[p->checking_subchannel]; selected = grpc_subchannel_get_connected_subchannel(selected_subchannel); GPR_ASSERT(selected != NULL); GRPC_CONNECTED_SUBCHANNEL_REF(selected, "picked_first"); /* drop the pick list: we are connected now */ GRPC_LB_POLICY_WEAK_REF(&p->base, "destroy_subchannels"); gpr_atm_rel_store(&p->selected, (gpr_atm)selected); grpc_exec_ctx_enqueue(exec_ctx, grpc_closure_create(destroy_subchannels, p), 1); /* update any calls that were waiting for a pick */ while ((pp = p->pending_picks)) { p->pending_picks = pp->next; *pp->target = selected; grpc_pollset_set_del_pollset(exec_ctx, &p->base.interested_parties, pp->pollset); grpc_exec_ctx_enqueue(exec_ctx, pp->on_complete, 1); gpr_free(pp); } grpc_connected_subchannel_notify_on_state_change( exec_ctx, selected, &p->base.interested_parties, &p->checking_connectivity, &p->connectivity_changed); break; case GRPC_CHANNEL_TRANSIENT_FAILURE: grpc_connectivity_state_set(exec_ctx, &p->state_tracker, GRPC_CHANNEL_TRANSIENT_FAILURE, "connecting_transient_failure"); p->checking_subchannel = (p->checking_subchannel + 1) % p->num_subchannels; p->checking_connectivity = grpc_subchannel_check_connectivity( p->subchannels[p->checking_subchannel]); if (p->checking_connectivity == GRPC_CHANNEL_TRANSIENT_FAILURE) { grpc_subchannel_notify_on_state_change( exec_ctx, p->subchannels[p->checking_subchannel], &p->base.interested_parties, &p->checking_connectivity, &p->connectivity_changed); } else { goto loop; } break; case GRPC_CHANNEL_CONNECTING: case GRPC_CHANNEL_IDLE: grpc_connectivity_state_set(exec_ctx, &p->state_tracker, GRPC_CHANNEL_CONNECTING, "connecting_changed"); grpc_subchannel_notify_on_state_change( exec_ctx, p->subchannels[p->checking_subchannel], &p->base.interested_parties, &p->checking_connectivity, &p->connectivity_changed); break; case GRPC_CHANNEL_FATAL_FAILURE: p->num_subchannels--; GPR_SWAP(grpc_subchannel *, p->subchannels[p->checking_subchannel], p->subchannels[p->num_subchannels]); GRPC_SUBCHANNEL_UNREF(exec_ctx, p->subchannels[p->num_subchannels], "pick_first"); if (p->num_subchannels == 0) { grpc_connectivity_state_set(exec_ctx, &p->state_tracker, GRPC_CHANNEL_FATAL_FAILURE, "no_more_channels"); while ((pp = p->pending_picks)) { p->pending_picks = pp->next; *pp->target = NULL; grpc_exec_ctx_enqueue(exec_ctx, pp->on_complete, 1); gpr_free(pp); } GRPC_LB_POLICY_WEAK_UNREF(exec_ctx, &p->base, "pick_first_connectivity"); } else { grpc_connectivity_state_set(exec_ctx, &p->state_tracker, GRPC_CHANNEL_TRANSIENT_FAILURE, "subchannel_failed"); p->checking_subchannel %= p->num_subchannels; p->checking_connectivity = grpc_subchannel_check_connectivity( p->subchannels[p->checking_subchannel]); goto loop; } } } gpr_mu_unlock(&p->mu); }
static void rr_connectivity_changed(grpc_exec_ctx *exec_ctx, void *arg, bool iomgr_success) { subchannel_data *sd = arg; round_robin_lb_policy *p = sd->policy; pending_pick *pp; ready_list *selected; int unref = 0; gpr_mu_lock(&p->mu); if (p->shutdown) { unref = 1; } else { switch (sd->connectivity_state) { case GRPC_CHANNEL_READY: grpc_connectivity_state_set(exec_ctx, &p->state_tracker, GRPC_CHANNEL_READY, "connecting_ready"); /* add the newly connected subchannel to the list of connected ones. * Note that it goes to the "end of the line". */ sd->ready_list_node = add_connected_sc_locked(p, sd->subchannel); /* at this point we know there's at least one suitable subchannel. Go * ahead and pick one and notify the pending suitors in * p->pending_picks. This preemtively replicates rr_pick()'s actions. */ selected = peek_next_connected_locked(p); if (p->pending_picks != NULL) { /* if the selected subchannel is going to be used for the pending * picks, update the last picked pointer */ advance_last_picked_locked(p); } while ((pp = p->pending_picks)) { p->pending_picks = pp->next; *pp->target = grpc_subchannel_get_connected_subchannel(selected->subchannel); if (grpc_lb_round_robin_trace) { gpr_log(GPR_DEBUG, "[RR CONN CHANGED] TARGET <-- SUBCHANNEL %p (NODE %p)", selected->subchannel, selected); } grpc_pollset_set_del_pollset(exec_ctx, p->base.interested_parties, pp->pollset); grpc_exec_ctx_enqueue(exec_ctx, pp->on_complete, true, NULL); gpr_free(pp); } grpc_subchannel_notify_on_state_change( exec_ctx, sd->subchannel, p->base.interested_parties, &sd->connectivity_state, &sd->connectivity_changed_closure); break; case GRPC_CHANNEL_CONNECTING: case GRPC_CHANNEL_IDLE: grpc_connectivity_state_set(exec_ctx, &p->state_tracker, sd->connectivity_state, "connecting_changed"); grpc_subchannel_notify_on_state_change( exec_ctx, sd->subchannel, p->base.interested_parties, &sd->connectivity_state, &sd->connectivity_changed_closure); break; case GRPC_CHANNEL_TRANSIENT_FAILURE: /* renew state notification */ grpc_subchannel_notify_on_state_change( exec_ctx, sd->subchannel, p->base.interested_parties, &sd->connectivity_state, &sd->connectivity_changed_closure); /* remove from ready list if still present */ if (sd->ready_list_node != NULL) { remove_disconnected_sc_locked(p, sd->ready_list_node); sd->ready_list_node = NULL; } grpc_connectivity_state_set(exec_ctx, &p->state_tracker, GRPC_CHANNEL_TRANSIENT_FAILURE, "connecting_transient_failure"); break; case GRPC_CHANNEL_FATAL_FAILURE: if (sd->ready_list_node != NULL) { remove_disconnected_sc_locked(p, sd->ready_list_node); sd->ready_list_node = NULL; } p->num_subchannels--; GPR_SWAP(subchannel_data *, p->subchannels[sd->index], p->subchannels[p->num_subchannels]); GRPC_SUBCHANNEL_UNREF(exec_ctx, sd->subchannel, "round_robin"); p->subchannels[sd->index]->index = sd->index; gpr_free(sd); unref = 1; if (p->num_subchannels == 0) { grpc_connectivity_state_set(exec_ctx, &p->state_tracker, GRPC_CHANNEL_FATAL_FAILURE, "no_more_channels"); while ((pp = p->pending_picks)) { p->pending_picks = pp->next; *pp->target = NULL; grpc_exec_ctx_enqueue(exec_ctx, pp->on_complete, true, NULL); gpr_free(pp); } } else { grpc_connectivity_state_set(exec_ctx, &p->state_tracker, GRPC_CHANNEL_TRANSIENT_FAILURE, "subchannel_failed"); } } /* switch */ } /* !unref */ gpr_mu_unlock(&p->mu); if (unref) { GRPC_LB_POLICY_WEAK_UNREF(exec_ctx, &p->base, "round_robin_connectivity"); } }
/* Pollset_set with an empty pollset */ void pollset_set_test_empty_pollset() { /* We construct the following structure for this test: * * +---> PS0 (EMPTY) * | * +---> FD0 * | * PSS0---+ * | +---> FD1 * | | * +---> PS1--+ * | * +---> FD2 */ grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; grpc_pollset_worker *worker; gpr_timespec deadline; test_fd tfds[3]; test_pollset pollsets[2]; test_pollset_set pollset_set; const int num_fds = GPR_ARRAY_SIZE(tfds); const int num_ps = GPR_ARRAY_SIZE(pollsets); const int num_pss = 1; init_test_fds(&exec_ctx, tfds, num_fds); init_test_pollsets(pollsets, num_ps); init_test_pollset_sets(&pollset_set, num_pss); /* Construct the structure */ grpc_pollset_set_add_fd(&exec_ctx, pollset_set.pss, tfds[0].fd); grpc_pollset_add_fd(&exec_ctx, pollsets[1].ps, tfds[1].fd); grpc_pollset_add_fd(&exec_ctx, pollsets[1].ps, tfds[2].fd); grpc_pollset_set_add_pollset(&exec_ctx, pollset_set.pss, pollsets[0].ps); grpc_pollset_set_add_pollset(&exec_ctx, pollset_set.pss, pollsets[1].ps); /* Test. Make all FDs readable and make sure that can be observed by doing * grpc_pollset_work on the empty pollset 'PS0' */ make_test_fds_readable(tfds, num_fds); gpr_mu_lock(pollsets[0].mu); deadline = grpc_timeout_milliseconds_to_deadline(2); GPR_ASSERT(GRPC_ERROR_NONE == grpc_pollset_work(&exec_ctx, pollsets[0].ps, &worker, gpr_now(GPR_CLOCK_MONOTONIC), deadline)); gpr_mu_unlock(pollsets[0].mu); grpc_exec_ctx_flush(&exec_ctx); verify_readable_and_reset(&exec_ctx, tfds, num_fds); grpc_exec_ctx_flush(&exec_ctx); /* Tear down */ grpc_pollset_set_del_fd(&exec_ctx, pollset_set.pss, tfds[0].fd); grpc_pollset_set_del_pollset(&exec_ctx, pollset_set.pss, pollsets[0].ps); grpc_pollset_set_del_pollset(&exec_ctx, pollset_set.pss, pollsets[1].ps); grpc_exec_ctx_flush(&exec_ctx); cleanup_test_fds(&exec_ctx, tfds, num_fds); cleanup_test_pollsets(&exec_ctx, pollsets, num_ps); cleanup_test_pollset_sets(&exec_ctx, &pollset_set, num_pss); grpc_exec_ctx_finish(&exec_ctx); }
/* Test some typical scenarios in pollset_set */ static void pollset_set_test_basic() { /* We construct the following structure for this test: * * +---> FD0 (Added before PSS1, PS1 and PS2 are added to PSS0) * | * +---> FD5 (Added after PSS1, PS1 and PS2 are added to PSS0) * | * | * | +---> FD1 (Added before PSS1 is added to PSS0) * | | * | +---> FD6 (Added after PSS1 is added to PSS0) * | | * +---> PSS1--+ +--> FD2 (Added before PS0 is added to PSS1) * | | | * | +---> PS0---+ * | | * PSS0---+ +--> FD7 (Added after PS0 is added to PSS1) * | * | * | +---> FD3 (Added before PS1 is added to PSS0) * | | * +---> PS1---+ * | | * | +---> FD8 (Added after PS1 added to PSS0) * | * | * | +---> FD4 (Added before PS2 is added to PSS0) * | | * +---> PS2---+ * | * +---> FD9 (Added after PS2 is added to PSS0) */ grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; grpc_pollset_worker *worker; gpr_timespec deadline; test_fd tfds[10]; test_pollset pollsets[3]; test_pollset_set pollset_sets[2]; const int num_fds = GPR_ARRAY_SIZE(tfds); const int num_ps = GPR_ARRAY_SIZE(pollsets); const int num_pss = GPR_ARRAY_SIZE(pollset_sets); init_test_fds(&exec_ctx, tfds, num_fds); init_test_pollsets(pollsets, num_ps); init_test_pollset_sets(pollset_sets, num_pss); /* Construct the pollset_set/pollset/fd tree (see diagram above) */ grpc_pollset_set_add_fd(&exec_ctx, pollset_sets[0].pss, tfds[0].fd); grpc_pollset_set_add_fd(&exec_ctx, pollset_sets[1].pss, tfds[1].fd); grpc_pollset_add_fd(&exec_ctx, pollsets[0].ps, tfds[2].fd); grpc_pollset_add_fd(&exec_ctx, pollsets[1].ps, tfds[3].fd); grpc_pollset_add_fd(&exec_ctx, pollsets[2].ps, tfds[4].fd); grpc_pollset_set_add_pollset_set(&exec_ctx, pollset_sets[0].pss, pollset_sets[1].pss); grpc_pollset_set_add_pollset(&exec_ctx, pollset_sets[1].pss, pollsets[0].ps); grpc_pollset_set_add_pollset(&exec_ctx, pollset_sets[0].pss, pollsets[1].ps); grpc_pollset_set_add_pollset(&exec_ctx, pollset_sets[0].pss, pollsets[2].ps); grpc_pollset_set_add_fd(&exec_ctx, pollset_sets[0].pss, tfds[5].fd); grpc_pollset_set_add_fd(&exec_ctx, pollset_sets[1].pss, tfds[6].fd); grpc_pollset_add_fd(&exec_ctx, pollsets[0].ps, tfds[7].fd); grpc_pollset_add_fd(&exec_ctx, pollsets[1].ps, tfds[8].fd); grpc_pollset_add_fd(&exec_ctx, pollsets[2].ps, tfds[9].fd); grpc_exec_ctx_flush(&exec_ctx); /* Test that if any FD in the above structure is readable, it is observable by * doing grpc_pollset_work on any pollset * * For every pollset, do the following: * - (Ensure that all FDs are in reset state) * - Make all FDs readable * - Call grpc_pollset_work() on the pollset * - Flush the exec_ctx * - Verify that on_readable call back was called for all FDs (and * reset the FDs) * */ for (int i = 0; i < num_ps; i++) { make_test_fds_readable(tfds, num_fds); gpr_mu_lock(pollsets[i].mu); deadline = grpc_timeout_milliseconds_to_deadline(2); GPR_ASSERT(GRPC_ERROR_NONE == grpc_pollset_work(&exec_ctx, pollsets[i].ps, &worker, gpr_now(GPR_CLOCK_MONOTONIC), deadline)); gpr_mu_unlock(pollsets[i].mu); grpc_exec_ctx_flush(&exec_ctx); verify_readable_and_reset(&exec_ctx, tfds, num_fds); grpc_exec_ctx_flush(&exec_ctx); } /* Test tear down */ grpc_pollset_set_del_fd(&exec_ctx, pollset_sets[0].pss, tfds[0].fd); grpc_pollset_set_del_fd(&exec_ctx, pollset_sets[0].pss, tfds[5].fd); grpc_pollset_set_del_fd(&exec_ctx, pollset_sets[1].pss, tfds[1].fd); grpc_pollset_set_del_fd(&exec_ctx, pollset_sets[1].pss, tfds[6].fd); grpc_exec_ctx_flush(&exec_ctx); grpc_pollset_set_del_pollset(&exec_ctx, pollset_sets[1].pss, pollsets[0].ps); grpc_pollset_set_del_pollset(&exec_ctx, pollset_sets[0].pss, pollsets[1].ps); grpc_pollset_set_del_pollset(&exec_ctx, pollset_sets[0].pss, pollsets[2].ps); grpc_pollset_set_del_pollset_set(&exec_ctx, pollset_sets[0].pss, pollset_sets[1].pss); grpc_exec_ctx_flush(&exec_ctx); cleanup_test_fds(&exec_ctx, tfds, num_fds); cleanup_test_pollsets(&exec_ctx, pollsets, num_ps); cleanup_test_pollset_sets(&exec_ctx, pollset_sets, num_pss); grpc_exec_ctx_finish(&exec_ctx); }