Fix unref-while-lock-held bug. Only shut down handshaker if in progress.

reviewable/pr8782/r2
Mark D. Roth 8 years ago
parent 0610434185
commit a9bd94335c
  1. 35
      src/core/lib/channel/handshaker.c

@ -141,7 +141,8 @@ void grpc_handshake_manager_destroy(grpc_exec_ctx* exec_ctx,
void grpc_handshake_manager_shutdown(grpc_exec_ctx* exec_ctx, void grpc_handshake_manager_shutdown(grpc_exec_ctx* exec_ctx,
grpc_handshake_manager* mgr) { grpc_handshake_manager* mgr) {
gpr_mu_lock(&mgr->mu); gpr_mu_lock(&mgr->mu);
if (mgr->index > 0) { // Shutdown the handshaker that's currently in progress, if any.
if (mgr->index > 0 && mgr->index <= mgr->count) {
grpc_handshaker_shutdown(exec_ctx, mgr->handshakers[mgr->index - 1]); grpc_handshaker_shutdown(exec_ctx, mgr->handshakers[mgr->index - 1]);
} }
gpr_mu_unlock(&mgr->mu); gpr_mu_unlock(&mgr->mu);
@ -149,28 +150,27 @@ void grpc_handshake_manager_shutdown(grpc_exec_ctx* exec_ctx,
// Helper function to call either the next handshaker or the // Helper function to call either the next handshaker or the
// on_handshake_done callback. // on_handshake_done callback.
static void call_next_handshaker_locked(grpc_exec_ctx* exec_ctx, // Returns true if we've scheduled the on_handshake_done callback.
static bool call_next_handshaker_locked(grpc_exec_ctx* exec_ctx,
grpc_handshake_manager* mgr, grpc_handshake_manager* mgr,
grpc_error* error) { grpc_error* error) {
GPR_ASSERT(mgr->index <= mgr->count); GPR_ASSERT(mgr->index <= mgr->count);
// If we got an error or we've finished the last handshaker, invoke // If we got an error or we've finished the last handshaker, invoke
// the on_handshake_done callback. Otherwise, call the next handshaker. // the on_handshake_done callback. Otherwise, call the next handshaker.
bool done = false;
if (error != GRPC_ERROR_NONE || mgr->index == mgr->count) { if (error != GRPC_ERROR_NONE || mgr->index == mgr->count) {
// Cancel deadline timer, since we're invoking the on_handshake_done // Cancel deadline timer, since we're invoking the on_handshake_done
// callback now. // callback now.
grpc_timer_cancel(exec_ctx, &mgr->deadline_timer); grpc_timer_cancel(exec_ctx, &mgr->deadline_timer);
grpc_exec_ctx_sched(exec_ctx, &mgr->on_handshake_done, error, NULL); grpc_exec_ctx_sched(exec_ctx, &mgr->on_handshake_done, error, NULL);
// Since we're invoking the final callback, we won't be coming back done = true;
// to this function, so we can release our reference to the } else {
// handshake manager. grpc_handshaker_do_handshake(exec_ctx, mgr->handshakers[mgr->index],
grpc_handshake_manager_unref(exec_ctx, mgr); mgr->acceptor, &mgr->call_next_handshaker,
return; &mgr->args);
} }
// Call the next handshaker.
grpc_handshaker_do_handshake(exec_ctx, mgr->handshakers[mgr->index],
mgr->acceptor, &mgr->call_next_handshaker,
&mgr->args);
++mgr->index; ++mgr->index;
return done;
} }
// A function used as the handshaker-done callback when chaining // A function used as the handshaker-done callback when chaining
@ -179,8 +179,14 @@ static void call_next_handshaker(grpc_exec_ctx* exec_ctx, void* arg,
grpc_error* error) { grpc_error* error) {
grpc_handshake_manager* mgr = arg; grpc_handshake_manager* mgr = arg;
gpr_mu_lock(&mgr->mu); gpr_mu_lock(&mgr->mu);
call_next_handshaker_locked(exec_ctx, mgr, GRPC_ERROR_REF(error)); bool done = call_next_handshaker_locked(exec_ctx, mgr, GRPC_ERROR_REF(error));
gpr_mu_unlock(&mgr->mu); gpr_mu_unlock(&mgr->mu);
// If we're invoked the final callback, we won't be coming back
// to this function, so we can release our reference to the
// handshake manager.
if (done) {
grpc_handshake_manager_unref(exec_ctx, mgr);
}
} }
// Callback invoked when deadline is exceeded. // Callback invoked when deadline is exceeded.
@ -217,6 +223,9 @@ void grpc_handshake_manager_do_handshake(
on_timeout, mgr, gpr_now(GPR_CLOCK_MONOTONIC)); on_timeout, mgr, gpr_now(GPR_CLOCK_MONOTONIC));
// Start first handshaker, which also owns a ref. // Start first handshaker, which also owns a ref.
gpr_ref(&mgr->refs); gpr_ref(&mgr->refs);
call_next_handshaker_locked(exec_ctx, mgr, GRPC_ERROR_NONE); bool done = call_next_handshaker_locked(exec_ctx, mgr, GRPC_ERROR_NONE);
gpr_mu_unlock(&mgr->mu); gpr_mu_unlock(&mgr->mu);
if (done) {
grpc_handshake_manager_unref(exec_ctx, mgr);
}
} }

Loading…
Cancel
Save