mirror of
https://github.com/ruby/ruby.git
synced 2026-10-28 08:25:20 +08:00
Ractor: widen the Ractor id to 64 bits
A Ractor id must stay unique for the life of the process, but the 32 bit counter wraps after ~4e9 Ractors and comes back onto ids that are still in use. That is not out of reach: a few thousand Ractor.new/s is days, not years. The wrapped value is 0 too, which some callers read as "the main Ractor". There is no portable 64 bit atomic, so RUBY_ATOMIC_FETCH_ADD gives way to the VM lock, which Ractor.new acquires a few frames later anyway. That lock is a no-op until there is a second Ractor, which is exactly when the GVL alone already serializes the counter, so ractor_next_id asserts the same condition vm_insert_ractor0 does. rb_serial_t, to match ec->ractor_id and bmethod's defined_ractor_id. Neither struct grows: rb_ractor_pub had 4 bytes of padding after id. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
aa7bc48bb1
commit
acc5cd03d9
8 files changed
+36
-33
Notes:
git
2026-09-10 07:03:36 +00:00
No files matched your search
@@ -5800,7 +5800,7 @@ rb_raw_obj_info_buitin_type(char *const buff, const size_t buff_size, const VALU
|
||||
else if (rb_ractor_p(obj)) {
|
||||
rb_ractor_t *r = (void *)DATA_PTR(obj);
|
||||
if (r) {
|
||||
APPEND_F("r:%d", r->pub.id);
|
||||
APPEND_F("r:%"PRI_SERIALT_PREFIX"u", r->pub.id);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -79,7 +79,7 @@ ASSERT_ractor_locking(rb_ractor_t *r)
|
||||
static void
|
||||
ractor_lock(rb_ractor_t *r, const char *file, int line)
|
||||
{
|
||||
RUBY_DEBUG_LOG2(file, line, "locking r:%u%s", r->pub.id, rb_current_ractor_raw(false) == r ? " (self)" : "");
|
||||
RUBY_DEBUG_LOG2(file, line, "locking r:%"PRI_SERIALT_PREFIX"u%s", r->pub.id, rb_current_ractor_raw(false) == r ? " (self)" : "");
|
||||
|
||||
ASSERT_ractor_unlocking(r);
|
||||
rb_native_mutex_lock(&r->sync.lock);
|
||||
@@ -98,7 +98,7 @@ ractor_lock(rb_ractor_t *r, const char *file, int line)
|
||||
}
|
||||
#endif
|
||||
|
||||
RUBY_DEBUG_LOG2(file, line, "locked r:%u%s", r->pub.id, rb_current_ractor_raw(false) == r ? " (self)" : "");
|
||||
RUBY_DEBUG_LOG2(file, line, "locked r:%"PRI_SERIALT_PREFIX"u%s", r->pub.id, rb_current_ractor_raw(false) == r ? " (self)" : "");
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -128,7 +128,7 @@ ractor_unlock(rb_ractor_t *r, const char *file, int line)
|
||||
|
||||
rb_native_mutex_unlock(&r->sync.lock);
|
||||
|
||||
RUBY_DEBUG_LOG2(file, line, "r:%u%s", r->pub.id, rb_current_ractor_raw(false) == r ? " (self)" : "");
|
||||
RUBY_DEBUG_LOG2(file, line, "r:%"PRI_SERIALT_PREFIX"u%s", r->pub.id, rb_current_ractor_raw(false) == r ? " (self)" : "");
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -175,7 +175,7 @@ ractor_status_str(enum ractor_status status)
|
||||
static void
|
||||
ractor_status_set(rb_ractor_t *r, enum ractor_status status)
|
||||
{
|
||||
RUBY_DEBUG_LOG("r:%u [%s]->[%s]", r->pub.id, ractor_status_str(r->status_), ractor_status_str(status));
|
||||
RUBY_DEBUG_LOG("r:%"PRI_SERIALT_PREFIX"u [%s]->[%s]", r->pub.id, ractor_status_str(r->status_), ractor_status_str(status));
|
||||
|
||||
// check 1
|
||||
if (r->status_ != ractor_created) {
|
||||
@@ -424,7 +424,7 @@ static void
|
||||
ractor_free(void *ptr)
|
||||
{
|
||||
rb_ractor_t *r = (rb_ractor_t *)ptr;
|
||||
RUBY_DEBUG_LOG("free r:%d", rb_ractor_id(r));
|
||||
RUBY_DEBUG_LOG("free r:%"PRI_SERIALT_PREFIX"u", rb_ractor_id(r));
|
||||
|
||||
free_targeted_hooks(&r->pub.targeted_hooks);
|
||||
rb_thread_sched_destroy(&r->threads.sched);
|
||||
@@ -520,26 +520,27 @@ RACTOR_PTR(VALUE self)
|
||||
}
|
||||
|
||||
#define MAIN_RACTOR_ID 1
|
||||
static rb_atomic_t ractor_last_id = MAIN_RACTOR_ID;
|
||||
static rb_serial_t ractor_last_id = MAIN_RACTOR_ID;
|
||||
|
||||
#include "ractor_sync.c"
|
||||
|
||||
// creation/termination
|
||||
|
||||
static uint32_t
|
||||
/* Ids are never reused, so they must not wrap either: 64 bits, which rules out an
|
||||
* atomic (there is no portable 64-bit one). Serialized by the VM lock, or by the
|
||||
* GVL before there is a second Ractor to take it against -- the same condition
|
||||
* vm_insert_ractor0 asserts. */
|
||||
static rb_serial_t
|
||||
ractor_next_id(void)
|
||||
{
|
||||
uint32_t id;
|
||||
|
||||
id = (uint32_t)(RUBY_ATOMIC_FETCH_ADD(ractor_last_id, 1) + 1);
|
||||
|
||||
return id;
|
||||
VM_ASSERT(RB_VM_LOCKED_P() || !rb_multi_ractor_p());
|
||||
return ++ractor_last_id;
|
||||
}
|
||||
|
||||
static void
|
||||
vm_insert_ractor0(rb_vm_t *vm, rb_ractor_t *r, bool single_ractor_mode)
|
||||
{
|
||||
RUBY_DEBUG_LOG("r:%u ractor.cnt:%u++", r->pub.id, vm->ractor.cnt);
|
||||
RUBY_DEBUG_LOG("r:%"PRI_SERIALT_PREFIX"u ractor.cnt:%u++", r->pub.id, vm->ractor.cnt);
|
||||
VM_ASSERT(single_ractor_mode || RB_VM_LOCKED_P());
|
||||
|
||||
/* Incremental marking only runs in a single-objspace world, and nothing later can
|
||||
@@ -846,8 +847,10 @@ ractor_create(rb_execution_context_t *ec, VALUE self, VALUE loc, VALUE name, VAL
|
||||
rb_ractor_t *r = RACTOR_PTR(rv);
|
||||
ractor_init(r, name, loc);
|
||||
|
||||
r->pub.id = ractor_next_id();
|
||||
RUBY_DEBUG_LOG("r:%u", r->pub.id);
|
||||
RB_VM_LOCKING() {
|
||||
r->pub.id = ractor_next_id();
|
||||
}
|
||||
RUBY_DEBUG_LOG("r:%"PRI_SERIALT_PREFIX"u", r->pub.id);
|
||||
|
||||
rb_ractor_t *cr = rb_ec_ractor_ptr(ec);
|
||||
r->verbose = cr->verbose;
|
||||
@@ -973,7 +976,7 @@ rb_ractor_living_threads_insert(rb_ractor_t *r, rb_thread_t *th)
|
||||
|
||||
RACTOR_LOCK(r);
|
||||
{
|
||||
RUBY_DEBUG_LOG("r(%d)->threads.cnt:%d++", r->pub.id, r->threads.cnt);
|
||||
RUBY_DEBUG_LOG("r(%"PRI_SERIALT_PREFIX"u)->threads.cnt:%d++", r->pub.id, r->threads.cnt);
|
||||
ccan_list_add_tail(&r->threads.set, &th->lt_node);
|
||||
r->threads.cnt++;
|
||||
}
|
||||
@@ -1145,7 +1148,7 @@ ractor_terminal_interrupt_all(rb_vm_t *vm)
|
||||
rb_ractor_t *r = 0;
|
||||
ccan_list_for_each(&vm->ractor.set, r, vmlr_node) {
|
||||
if (r != vm->ractor.main_ractor) {
|
||||
RUBY_DEBUG_LOG("r:%d", rb_ractor_id(r));
|
||||
RUBY_DEBUG_LOG("r:%"PRI_SERIALT_PREFIX"u", rb_ractor_id(r));
|
||||
rb_ractor_terminate_interrupt_main_thread(r);
|
||||
}
|
||||
}
|
||||
@@ -1362,7 +1365,7 @@ rb_ractor_dump(void)
|
||||
|
||||
ccan_list_for_each(&vm->ractor.set, r, vmlr_node) {
|
||||
if (r != vm->ractor.main_ractor) {
|
||||
fprintf(stderr, "r:%u (%s)\n", r->pub.id, ractor_status_str(r->status_));
|
||||
fprintf(stderr, "r:%"PRI_SERIALT_PREFIX"u (%s)\n", r->pub.id, ractor_status_str(r->status_));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -383,7 +383,7 @@ class Ractor
|
||||
def inspect
|
||||
loc = __builtin_cexpr! %q{ RACTOR_PTR(self)->loc }
|
||||
name = __builtin_cexpr! %q{ RACTOR_PTR(self)->name }
|
||||
id = __builtin_cexpr! %q{ UINT2NUM(rb_ractor_id(RACTOR_PTR(self))) }
|
||||
id = __builtin_cexpr! %q{ ULL2NUM(rb_ractor_id(RACTOR_PTR(self))) }
|
||||
status = __builtin_cexpr! %q{
|
||||
rb_str_new2(RACTOR_PTR(self)->status_ == ractor_terminated ? "terminated" : "running")
|
||||
}
|
||||
@@ -866,7 +866,7 @@ class Ractor
|
||||
# port.inspect -> string
|
||||
def inspect
|
||||
"#<Ractor::Port to:\##{
|
||||
__builtin_cexpr! "SIZET2NUM(rb_ractor_id(ractor_port_ptr_check(self)->r))"
|
||||
__builtin_cexpr! "ULL2NUM(rb_ractor_id(ractor_port_ptr_check(self)->r))"
|
||||
} id:#{
|
||||
__builtin_cexpr! "SIZET2NUM(ractor_port_id(RACTOR_PORT_PTR(self)))"
|
||||
}>"
|
||||
|
||||
+1
-1
@@ -334,7 +334,7 @@ rb_ractor_set_current_ec_(rb_ractor_t *cr, rb_execution_context_t *ec, const cha
|
||||
void rb_vm_ractor_blocking_cnt_inc(rb_vm_t *vm, rb_ractor_t *cr, const char *file, int line);
|
||||
void rb_vm_ractor_blocking_cnt_dec(rb_vm_t *vm, rb_ractor_t *cr, const char *file, int line);
|
||||
|
||||
static inline uint32_t
|
||||
static inline rb_serial_t
|
||||
rb_ractor_id(const rb_ractor_t *r)
|
||||
{
|
||||
return r->pub.id;
|
||||
|
||||
+3
-3
@@ -1359,7 +1359,7 @@ ractor_wakeup_all(rb_ractor_t *r, enum ractor_wakeup_status wakeup_status)
|
||||
{
|
||||
ASSERT_ractor_unlocking(r);
|
||||
|
||||
RUBY_DEBUG_LOG("r:%u wakeup:%s", rb_ractor_id(r), wakeup_status_str(wakeup_status));
|
||||
RUBY_DEBUG_LOG("r:%"PRI_SERIALT_PREFIX"u wakeup:%s", rb_ractor_id(r), wakeup_status_str(wakeup_status));
|
||||
|
||||
bool wakeup_p = false;
|
||||
|
||||
@@ -1622,7 +1622,7 @@ ractor_send_basket(rb_execution_context_t *ec, const struct ractor_port *rp, str
|
||||
{
|
||||
bool closed = false;
|
||||
|
||||
RUBY_DEBUG_LOG("port:%u@r%u b:%s v:%p", (unsigned int)ractor_port_id(rp), rb_ractor_id(rp->r), basket_type_name(b->type), (void *)b->p.v);
|
||||
RUBY_DEBUG_LOG("port:%u@r%"PRI_SERIALT_PREFIX"u b:%s v:%p", (unsigned int)ractor_port_id(rp), rb_ractor_id(rp->r), basket_type_name(b->type), (void *)b->p.v);
|
||||
|
||||
RACTOR_LOCK(rp->r);
|
||||
{
|
||||
@@ -1644,7 +1644,7 @@ ractor_send_basket(rb_execution_context_t *ec, const struct ractor_port *rp, str
|
||||
ractor_wakeup_all(rp->r, wakeup_by_send);
|
||||
}
|
||||
else {
|
||||
RUBY_DEBUG_LOG("closed:%u@r%u", (unsigned int)ractor_port_id(rp), rb_ractor_id(rp->r));
|
||||
RUBY_DEBUG_LOG("closed:%u@r%"PRI_SERIALT_PREFIX"u", (unsigned int)ractor_port_id(rp), rb_ractor_id(rp->r));
|
||||
|
||||
/* Nothing took the basket: it was not enqueued, so free it whether or not the
|
||||
* caller wants the error raised. */
|
||||
|
||||
@@ -976,7 +976,7 @@ thread_create_core(VALUE thval, struct thread_create_params *params)
|
||||
RBASIC_CLEAR_CLASS(th->pending_interrupt_mask_stack);
|
||||
}
|
||||
|
||||
RUBY_DEBUG_LOG("r:%u th:%u", rb_ractor_id(th->ractor), rb_th_serial(th));
|
||||
RUBY_DEBUG_LOG("r:%"PRI_SERIALT_PREFIX"u th:%u", rb_ractor_id(th->ractor), rb_th_serial(th));
|
||||
|
||||
rb_ractor_living_threads_insert(th->ractor, th);
|
||||
|
||||
|
||||
+6
-6
@@ -168,7 +168,7 @@ ractor_sched_dump_(const char *file, int line, rb_vm_t *vm)
|
||||
ccan_list_for_each(&vm->ractor.sched.grq, r, threads.sched.grq_node) {
|
||||
i++;
|
||||
if (i>10) rb_bug("!!");
|
||||
fprintf(stderr, " %d ready:%d\n", i, rb_ractor_id(r));
|
||||
fprintf(stderr, " %d ready:%"PRI_SERIALT_PREFIX"u\n", i, rb_ractor_id(r));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -248,7 +248,7 @@ ASSERT_thread_sched_locked(struct rb_thread_sched *sched, rb_thread_t *th)
|
||||
|
||||
|
||||
RBIMPL_ATTR_MAYBE_UNUSED()
|
||||
static unsigned int
|
||||
static rb_serial_t
|
||||
rb_ractor_serial(const rb_ractor_t *r)
|
||||
{
|
||||
if (r) {
|
||||
@@ -290,9 +290,9 @@ ractor_sched_lock_(rb_vm_t *vm, rb_ractor_t *cr, const char *file, int line)
|
||||
rb_native_mutex_lock(&vm->ractor.sched.lock);
|
||||
|
||||
#if VM_CHECK_MODE
|
||||
RUBY_DEBUG_LOG2(file, line, "cr:%u prev_owner:%u", rb_ractor_serial(cr), rb_ractor_serial(vm->ractor.sched.lock_owner));
|
||||
RUBY_DEBUG_LOG2(file, line, "cr:%"PRI_SERIALT_PREFIX"u prev_owner:%"PRI_SERIALT_PREFIX"u", rb_ractor_serial(cr), rb_ractor_serial(vm->ractor.sched.lock_owner));
|
||||
#else
|
||||
RUBY_DEBUG_LOG2(file, line, "cr:%u", rb_ractor_serial(cr));
|
||||
RUBY_DEBUG_LOG2(file, line, "cr:%"PRI_SERIALT_PREFIX"u", rb_ractor_serial(cr));
|
||||
#endif
|
||||
|
||||
ractor_sched_set_locked(vm, cr);
|
||||
@@ -301,7 +301,7 @@ ractor_sched_lock_(rb_vm_t *vm, rb_ractor_t *cr, const char *file, int line)
|
||||
static void
|
||||
ractor_sched_unlock_(rb_vm_t *vm, rb_ractor_t *cr, const char *file, int line)
|
||||
{
|
||||
RUBY_DEBUG_LOG2(file, line, "cr:%u", rb_ractor_serial(cr));
|
||||
RUBY_DEBUG_LOG2(file, line, "cr:%"PRI_SERIALT_PREFIX"u", rb_ractor_serial(cr));
|
||||
|
||||
ractor_sched_set_unlocked(vm, cr);
|
||||
rb_native_mutex_unlock(&vm->ractor.sched.lock);
|
||||
@@ -1323,7 +1323,7 @@ ractor_sched_enq(rb_vm_t *vm, rb_ractor_t *r)
|
||||
vm->ractor.sched.grq_cnt++;
|
||||
VM_ASSERT(grq_size(vm, cr) == vm->ractor.sched.grq_cnt);
|
||||
|
||||
RUBY_DEBUG_LOG("r:%u th:%u grq_cnt:%u", rb_ractor_id(r), rb_th_serial(sched->running), vm->ractor.sched.grq_cnt);
|
||||
RUBY_DEBUG_LOG("r:%"PRI_SERIALT_PREFIX"u th:%u grq_cnt:%u", rb_ractor_id(r), rb_th_serial(sched->running), vm->ractor.sched.grq_cnt);
|
||||
|
||||
rb_native_cond_signal(&vm->ractor.sched.cond);
|
||||
|
||||
|
||||
@@ -2433,7 +2433,7 @@ rb_exec_event_hook_orig(rb_execution_context_t *ec, rb_hook_list_t *hooks, rb_ev
|
||||
|
||||
struct rb_ractor_pub {
|
||||
VALUE self;
|
||||
uint32_t id;
|
||||
rb_serial_t id;
|
||||
rb_hook_list_t hooks;
|
||||
st_table targeted_hooks; // also called "local hooks". {ISEQ => hook_list, def => hook_list...}
|
||||
unsigned int targeted_hooks_cnt; // ex: tp.enabled(target: method(:puts))
|
||||
|
||||
Reference in new issue
Block a user