Skip to content

Commit 14a8791

Browse files
committed
client: happy eyeballs: retire the primary socket when a racer wins
When a racing (parallel) connect is seen to have completed in the LCCCR_CONNECTED path, promote_parallel_fd() moves wsi->desc and wsi->position_in_fds_table to the racer, but nothing removes the primary socket from the fds table or closes it. Its slot keeps mapping the primary fd to this wsi while the wsi's position_in_fds_table now points at the racer's slot, so the slot can never be removed: __remove_wsi_socket_from_fds() on this wsi will always target the racer's slot instead. The observable effect is that pt->fds never drains, so the while (pt->fds_count) { struct lws *wsi = wsi_from_fd(context, pt->fds[0].fd); ... } loop in lws_context_destroy() spins forever at 100% CPU, and the primary socket is leaked. Retire the primary before promoting, the same way the conn_good is_parallel path already does (remove from fds, then promote_parallel or close). Reached easily with an event lib that implements the parallel ops: on Windows the win32 connect-check sul probes racer fds directly, and on POSIX once the racing fd actually gets its POLLOUT.
1 parent 5a86bc7 commit 14a8791

1 file changed

Lines changed: 26 additions & 1 deletion

File tree

lib/core-net/client/connect3.c

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -493,8 +493,33 @@ lws_client_connect_3_connect(struct lws *wsi, const char *ads,
493493
return NULL;
494494
}
495495
lws_sul_cancel(&wsi->sul_happy_eyeballs);
496-
if (pidx != -1)
496+
if (pidx != -1) {
497+
/*
498+
* A racing connect won. The primary
499+
* socket is still open and still in the
500+
* fds table, and promote_parallel_fd()
501+
* is about to move the wsi's
502+
* position_in_fds_table to the racer's
503+
* slot, orphaning the primary's slot: it
504+
* still maps the primary fd to this wsi,
505+
* so pt->fds can never drain and eg the
506+
* "while (pt->fds_count)" loop in
507+
* lws_context_destroy() spins forever.
508+
*
509+
* Retire the primary the same way the
510+
* conn_good is_parallel path does.
511+
*/
512+
lws_pt_lock(pt, __func__);
513+
__remove_wsi_socket_from_fds(wsi);
514+
lws_pt_unlock(pt);
515+
516+
if (wsi->a.context->event_loop_ops->promote_parallel)
517+
wsi->a.context->event_loop_ops->promote_parallel(wsi, pidx);
518+
else
519+
compatible_close(wsi->desc.sockfd);
520+
497521
promote_parallel_fd(wsi, pidx);
522+
}
498523
/* close all remaining parallel */
499524
for (m = 0; m < wsi->parallel_count; m++)
500525
if (wsi->parallel_conns[m].is_valid)

0 commit comments

Comments
 (0)