diff --git a/packages/bun-usockets/src/eventing/libuv.c b/packages/bun-usockets/src/eventing/libuv.c index e1eb0551b3ef..298be8254ee8 100644 --- a/packages/bun-usockets/src/eventing/libuv.c +++ b/packages/bun-usockets/src/eventing/libuv.c @@ -44,11 +44,7 @@ int us_internal_libuv_peer_reset_probe(LIBUS_SOCKET_DESCRIPTOR fd) { } static struct us_socket_t *us_internal_poll_cb_adopted_socket(struct us_poll_t *wp) { - struct us_socket_t *s = (struct us_socket_t *)wp; - if (s->flags.adopted && s->prev) { - s = s->prev; - } - return s; + return us_internal_socket_follow_adopted((struct us_socket_t *)wp); } static int us_internal_poll_cb_socket_is_probeable(struct us_poll_t *wp) { diff --git a/packages/bun-usockets/src/internal/internal.h b/packages/bun-usockets/src/internal/internal.h index 8444fa14b641..eca40c7d92c1 100644 --- a/packages/bun-usockets/src/internal/internal.h +++ b/packages/bun-usockets/src/internal/internal.h @@ -343,6 +343,21 @@ struct us_socket_t { _Static_assert(sizeof(struct us_socket_flags) == 1, "us_socket_flags grew"); #endif +/* us_socket_adopt relocates a socket whose ext grows and retires the old block + * (is_closed + adopted, prev -> replacement; freed by the outermost tick's + * us_internal_free_closed_sockets, so it is still readable mid-dispatch). A + * callback may adopt more than once before returning, retiring each block in + * turn, so walk the whole chain rather than one link: one link can land on a + * block that is itself retired. The walk ends on the live block, which never + * has adopted set (the copy is taken before the source is flagged). Tolerates + * NULL, which callbacks may return. */ +static inline struct us_socket_t *us_internal_socket_follow_adopted(struct us_socket_t *s) { + while (s && s->flags.adopted && s->prev) { + s = s->prev; + } + return s; +} + struct us_connecting_socket_t { alignas(LIBUS_EXT_ALIGNMENT) struct addrinfo_request *addrinfo_req; struct us_socket_group_t *group; diff --git a/packages/bun-usockets/src/loop.c b/packages/bun-usockets/src/loop.c index 7df70af56ea2..2d0d3cea33bf 100644 --- a/packages/bun-usockets/src/loop.c +++ b/packages/bun-usockets/src/loop.c @@ -555,9 +555,7 @@ void us_internal_dispatch_ready_poll(struct us_poll_t *p, int error, int eof, in us_dispatch_open(s, 0, bsd_addr_get_ip(&addr), bsd_addr_get_ip_length(&addr)); } /* After socket adoption, track the new socket; the old one becomes invalid */ - if(s && s->flags.adopted && s->prev) { - s = s->prev; - } + s = us_internal_socket_follow_adopted(s); /* When the kernel deferred the accept until data arrived (TCP_DEFER_ACCEPT * on Linux, SO_ACCEPTFILTER on FreeBSD), the request/ClientHello is already @@ -583,9 +581,7 @@ void us_internal_dispatch_ready_poll(struct us_poll_t *p, int error, int eof, in /* We should only use s, no p after this point */ struct us_socket_t *s = (struct us_socket_t *) p; /* After socket adoption, track the new socket; the old one becomes invalid */ - if(s && s->flags.adopted && s->prev) { - s = s->prev; - } + s = us_internal_socket_follow_adopted(s); /* The group can change after calling a callback but the loop is always the same */ struct us_loop_t* loop = s->group->loop; if (events & LIBUS_SOCKET_WRITABLE && !error) { @@ -601,9 +597,7 @@ void us_internal_dispatch_ready_poll(struct us_poll_t *p, int error, int eof, in s = s->ssl ? us_internal_ssl_on_writable(s) : us_dispatch_writable(s); /* After socket adoption, track the new socket; the old one becomes invalid */ - if(s && s->flags.adopted && s->prev) { - s = s->prev; - } + s = us_internal_socket_follow_adopted(s); if (!s || us_socket_is_closed(s)) { return; @@ -724,9 +718,7 @@ void us_internal_dispatch_ready_poll(struct us_poll_t *p, int error, int eof, in s = s->ssl ? us_internal_ssl_on_data(s, loop->data.recv_buf + LIBUS_RECV_BUFFER_PADDING, length) : us_dispatch_data(s, loop->data.recv_buf + LIBUS_RECV_BUFFER_PADDING, length); /* After socket adoption, track the new socket; the old one becomes invalid */ - if(s && s->flags.adopted && s->prev) { - s = s->prev; - } + s = us_internal_socket_follow_adopted(s); read_any = 1; // loop->num_ready_polls isn't accessible on Windows. #ifndef WIN32