ractor: lock the owner when Port#closed? is queried from another Ractor

ractor_closed_port_p asserts the owning ractor's lock is held for foreign
access and reads sync.ports via st_lookup, but Ractor::Port#closed?
(ractor_port_closed_p) called it without the lock. From a foreign Ractor this
tripped the assertion and raced the owner's st_insert/st_delete on the ports
table. Take the ractor lock for foreign queries, like every other foreign
reader.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Koichi Sasada 2026-07-02 20:53:02 +00:00
parent bf02cf1aae
commit 10e53afd4e
Notes: git 2026-07-03 03:25:21 +00:00

View File

@ -144,13 +144,27 @@ static VALUE
ractor_port_closed_p(rb_execution_context_t *ec, VALUE self)
{
const struct ractor_port *rp = RACTOR_PORT_PTR(self);
rb_ractor_t *r = rp->r;
bool closed;
if (ractor_closed_port_p(ec, rp->r, rp)) {
return Qtrue;
if (rb_ec_ractor_ptr(ec) == r) {
/* The owner's threads are serialized by the ractor GVL, so the ports
* table can't change under this lookup. */
closed = ractor_closed_port_p(ec, r, rp);
}
else {
return Qfalse;
/* A foreign Ractor races the owner's st_insert/st_delete on the ports
* table; take the lock like every other foreign reader. ractor_closed_port_p
* asserts the lock is held for foreign access, and Port#closed? was the
* only path reaching it without the lock. */
RACTOR_LOCK(r);
{
closed = ractor_closed_port_p(ec, r, rp);
}
RACTOR_UNLOCK(r);
}
return closed ? Qtrue : Qfalse;
}
static VALUE