fix(usockets) fix last_write_failed flag (#25496)

https://github.com/oven-sh/bun/pull/25361 needs to be merged before this
PR

## Summary
- Move `last_write_failed` flag from loop-level to per-socket flag for
correctness

## Changes

- Move `last_write_failed` from `loop->data` to `socket->flags`
- More semantically correct since write status is per-socket, not
per-loop

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Ciro Spaciari
2025-12-16 14:26:42 -08:00
committed by GitHub
parent 7c06320d0f
commit a1dd26d7db
8 changed files with 11 additions and 14 deletions

View File

@@ -417,8 +417,7 @@ void us_internal_update_handshake(struct us_internal_ssl_socket_t *s) {
}
s->handshake_state = HANDSHAKE_PENDING;
s->ssl_write_wants_read = 1;
s->s.context->loop->data.last_write_failed = 1;
s->s.flags.last_write_failed = 1;
return;
}
// success

View File

@@ -71,7 +71,7 @@ void us_poll_init(struct us_poll_t *p, LIBUS_SOCKET_DESCRIPTOR fd,
}
void us_poll_free(struct us_poll_t *p, struct us_loop_t *loop) {
// poll was adopted and dont own uv_poll_t anymore
// poll was resized and dont own uv_poll_t anymore
if(!p->uv_p) {
free(p);
return;

View File

@@ -176,6 +176,8 @@ struct us_socket_flags {
bool adopted: 1;
/* If true, the socket is a TLS socket */
bool is_tls: 1;
/* If true, the last write to this socket failed (would block) */
bool last_write_failed: 1;
} __attribute__((packed));

View File

@@ -37,7 +37,6 @@ struct us_internal_loop_data_t {
struct us_timer_t *sweep_timer;
int sweep_timer_count;
struct us_internal_async *wakeup_async;
int last_write_failed;
struct us_socket_context_t *head;
struct us_socket_context_t *iterator;
struct us_socket_context_t *closed_context_head;

View File

@@ -391,14 +391,12 @@ void us_internal_dispatch_ready_poll(struct us_poll_t *p, int error, int eof, in
/* The context can change after calling a callback but the loop is always the same */
struct us_loop_t* loop = s->context->loop;
if (events & LIBUS_SOCKET_WRITABLE && !error) {
/* Note: if we failed a write as a socket of one loop then adopted
* to another loop, this will be wrong. Absurd case though */
loop->data.last_write_failed = 0;
s->flags.last_write_failed = 0;
#ifdef LIBUS_USE_KQUEUE
/* Kqueue is one-shot so is not writable anymore */
p->state.poll_type = us_internal_poll_type(p) | ((events & LIBUS_SOCKET_READABLE) ? POLL_TYPE_POLLING_IN : 0);
#endif
s = s->context->on_writable(s);
/* After socket adoption, track the new socket; the old one becomes invalid */
if(s && s->flags.adopted && s->prev) {
@@ -410,7 +408,7 @@ void us_internal_dispatch_ready_poll(struct us_poll_t *p, int error, int eof, in
}
/* If we have no failed write or if we shut down, then stop polling for more writable */
if (!loop->data.last_write_failed || us_socket_is_shut_down(0, s)) {
if (!s->flags.last_write_failed || us_socket_is_shut_down(0, s)) {
us_poll_change(&s->p, loop, us_poll_events(&s->p) & LIBUS_SOCKET_READABLE);
} else {
#ifdef LIBUS_USE_KQUEUE

View File

@@ -370,7 +370,7 @@ int us_socket_write(int ssl, struct us_socket_t *s, const char *data, int length
int written = bsd_send(us_poll_fd(&s->p), data, length);
if (written != length) {
s->context->loop->data.last_write_failed = 1;
s->flags.last_write_failed = 1;
us_poll_change(&s->p, s->context->loop, LIBUS_SOCKET_READABLE | LIBUS_SOCKET_WRITABLE);
}
@@ -407,7 +407,7 @@ int us_socket_ipc_write_fd(struct us_socket_t *s, const char* data, int length,
int sent = bsd_sendmsg(us_poll_fd(&s->p), &msg, 0);
if (sent != length) {
s->context->loop->data.last_write_failed = 1;
s->flags.last_write_failed = 1;
us_poll_change(&s->p, s->context->loop, LIBUS_SOCKET_READABLE | LIBUS_SOCKET_WRITABLE);
}

View File

@@ -1701,7 +1701,7 @@ size_t uws_req_get_header(uws_req_t *res, const char *lower_case_header,
{
us_socket_r s = (us_socket_t *)res;
if(us_socket_is_closed(s->flags.is_tls, s)) return;
s->context->loop->data.last_write_failed = 1;
s->flags.last_write_failed = 1;
us_poll_change(&s->p, s->context->loop,
LIBUS_SOCKET_READABLE | LIBUS_SOCKET_WRITABLE);
}
@@ -1866,7 +1866,7 @@ __attribute__((callback (corker, ctx)))
void us_socket_sendfile_needs_more(us_socket_r s) {
if(us_socket_is_closed(s->flags.is_tls, s)) return;
s->context->loop->data.last_write_failed = 1;
s->flags.last_write_failed = 1;
us_poll_change(&s->p, s->context->loop, LIBUS_SOCKET_READABLE | LIBUS_SOCKET_WRITABLE);
}

View File

@@ -4,7 +4,6 @@ pub const InternalLoopData = extern struct {
sweep_timer: ?*Timer,
sweep_timer_count: i32,
wakeup_async: ?*us_internal_async,
last_write_failed: i32,
head: ?*SocketContext,
iterator: ?*SocketContext,
closed_context_head: ?*SocketContext,