in c/src/proactor/win_iocp.cpp [3335:3416]
void pn_proactor_disconnect(pn_proactor_t *p, pn_condition_t *cond) {
pcontext_t *disconnecting_pcontexts = NULL;
pcontext_t *ctx = NULL;
{
csguard g(&p->context.cslock);
// Move the whole contexts list into a disconnecting state
disconnecting_pcontexts = p->contexts;
p->contexts = NULL;
// First pass: mark each pcontext as disconnecting and update global pending count.
pcontext_t *ctx = disconnecting_pcontexts;
while (ctx) {
ctx->disconnecting = true;
ctx->disconnect_ops = 2; // Second pass below and proactor_remove(), in any order.
p->disconnects_pending++;
ctx = ctx->next;
}
}
// no lock
if (!disconnecting_pcontexts) {
csguard p_guard(&p->context.cslock);
wake_if_inactive(p);
return;
}
// Second pass: different locking, close the pcontexts, free them if !disconnect_ops
pcontext_t *next = disconnecting_pcontexts;
while (next) {
ctx = next;
next = ctx->next; /* Save next pointer in case we free ctx */
bool do_free = false;
pconnection_t *pc = pcontext_pconnection(ctx);
pn_listener_t *l = pc ? NULL : pcontext_listener(ctx);
CRITICAL_SECTION *ctx_cslock = pc ? &pc->context.cslock : &l->context.cslock;
csguard ctx_guard(ctx_cslock);
if (pc) {
pc->can_wake = false;
if (!ctx->closing) {
if (ctx->working) {
// Must defer
pc->queued_disconnect = true;
if (cond) {
if (!pc->disconnect_condition)
pc->disconnect_condition = pn_condition();
pn_condition_copy(pc->disconnect_condition, cond);
}
}
else {
// No conflicting working context.
if (cond) {
pn_condition_copy(pn_transport_condition(pc->driver.transport), cond);
}
pn_connection_driver_close(&pc->driver);
}
}
} else {
assert(l);
if (!ctx->closing) {
if (cond) {
pn_condition_copy(pn_listener_condition(l), cond);
}
listener_begin_close(l);
}
}
csguard p_guard(&p->context.cslock);
if (--ctx->disconnect_ops == 0) {
do_free = true;
wake_if_inactive(p);
} else {
// If initiating the close, wake the pcontext to do the free.
wakeup(pc ? &pc->psocket : l->psockets);
}
p_guard.release();
ctx_guard.release();
if (do_free) {
if (pc) pconnection_final_free(pc);
else listener_final_free(pcontext_listener(ctx));
}
}
}