mirror of
https://github.com/ruby/ruby.git
synced 2026-10-28 16:36:20 +08:00
Ractor: restrict class/module modification to the creator Ractor
Only the Ractor which created a class/module may modify it: define, remove or undefine methods, alias, include/prepend, refine, define or remove constants, register an autoload, and set the class's own instance variables. Reading is unchanged. This replaces the assorted "main Ractor only" rules with one creator-based rule, and in exchange a Ractor gets full use of the classes it creates itself -- unshareable constant and instance variable values included, which even the main Ractor could not do for its own classes before. Three paths reach a class's tables without going through rb_class_modify_check, so they get the check of their own: Module#refine (which writes its refinement tables into the receiver as well as into the refined class), Class#initialize (which writes the superclass of a not-yet-initialized class, and had no frozen check either), and Module#ruby2_keywords. The constant inline cache has to move with the rule. Its hit condition was "shareable value, or we are the main Ractor", which was sound only because a non-main Ractor could not have an unshareable constant in the first place. Now it can, so an owner priming the cache would let any other Ractor read that value straight out of it, never reaching the owner check in vm_get_ev_const. The entry records the Ractor which filled it instead -- the one that passed the check -- which also gives a non-main owner a fast path it never had. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
3f2e84dd36
commit
2e5bf429e4
10 files changed
+183
-60
Notes:
git
2026-09-10 07:03:35 +00:00
No files matched your search
+24
-17
@@ -1209,12 +1209,11 @@ rb_alias_variable(ID name1, ID name2)
|
||||
}
|
||||
|
||||
static void
|
||||
IVAR_ACCESSOR_SHOULD_BE_MAIN_RACTOR(ID id)
|
||||
class_ivar_set_ractor_check(VALUE klass, ID id)
|
||||
{
|
||||
if (UNLIKELY(!rb_ractor_main_p())) {
|
||||
if (rb_is_instance_id(id)) { // check only normal ivars
|
||||
rb_raise(rb_eRactorIsolationError, "can not set instance variables of classes/modules by non-main Ractors");
|
||||
}
|
||||
if (rb_is_instance_id(id) && // check only normal ivars
|
||||
UNLIKELY(!rb_class_owned_p(klass))) {
|
||||
rb_raise(rb_eRactorIsolationError, "can not set instance variables of classes/modules created by another Ractor");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1244,6 +1243,10 @@ ivar_ractor_check(VALUE obj, ID id)
|
||||
UNLIKELY(!rb_ractor_main_p()) &&
|
||||
UNLIKELY(rb_ractor_shareable_p(obj))) {
|
||||
|
||||
if (RB_TYPE_P(obj, T_CLASS) || RB_TYPE_P(obj, T_MODULE)) {
|
||||
// classes/modules are owner-checked at each read/write site instead
|
||||
return;
|
||||
}
|
||||
rb_raise(rb_eRactorIsolationError, "can not access instance variables of shareable objects from non-main Ractors");
|
||||
}
|
||||
}
|
||||
@@ -1556,11 +1559,11 @@ rb_ivar_lookup(VALUE obj, ID id, VALUE undef)
|
||||
}
|
||||
|
||||
if (is_class && val != undef && rb_is_instance_id(id)) {
|
||||
if (UNLIKELY(!rb_ractor_main_p()) && !rb_ractor_shareable_p(val)) {
|
||||
if (UNLIKELY(!rb_class_owned_p(obj)) && !rb_ractor_shareable_p(val)) {
|
||||
rb_raise(
|
||||
rb_eRactorIsolationError,
|
||||
"can not get unshareable values from instance variables of classes/modules from "
|
||||
"non-main Ractors (%"PRIsVALUE" from %"PRIsVALUE")",
|
||||
"can not get unshareable values from instance variables of classes/modules "
|
||||
"created by another Ractor (%"PRIsVALUE" from %"PRIsVALUE")",
|
||||
rb_id2str(id),
|
||||
obj
|
||||
);
|
||||
@@ -1593,9 +1596,9 @@ rb_ivar_get_at(VALUE obj, attr_index_t index, ID id)
|
||||
VALUE fields_obj = RCLASS_WRITABLE_FIELDS_OBJ(obj);
|
||||
VALUE val = rb_imemo_fields_ptr(fields_obj)[index];
|
||||
|
||||
if (UNLIKELY(!rb_ractor_main_p()) && !rb_ractor_shareable_p(val)) {
|
||||
if (UNLIKELY(!rb_class_owned_p(obj)) && !rb_ractor_shareable_p(val)) {
|
||||
rb_raise(rb_eRactorIsolationError,
|
||||
"can not get unshareable values from instance variables of classes/modules from non-main Ractors");
|
||||
"can not get unshareable values from instance variables of classes/modules created by another Ractor");
|
||||
}
|
||||
|
||||
return val;
|
||||
@@ -1648,7 +1651,7 @@ rb_ivar_delete(VALUE obj, ID id, VALUE undef)
|
||||
int type = BUILTIN_TYPE(obj);
|
||||
|
||||
if (type == T_CLASS || type == T_MODULE) {
|
||||
IVAR_ACCESSOR_SHOULD_BE_MAIN_RACTOR(id);
|
||||
class_ivar_set_ractor_check(obj, id);
|
||||
|
||||
if (rb_multi_ractor_p()) {
|
||||
concurrent = true;
|
||||
@@ -2042,7 +2045,7 @@ ivar_set(VALUE obj, ID id, VALUE val)
|
||||
case T_CLASS:
|
||||
case T_MODULE:
|
||||
{
|
||||
IVAR_ACCESSOR_SHOULD_BE_MAIN_RACTOR(id);
|
||||
class_ivar_set_ractor_check(obj, id);
|
||||
bool dontcare;
|
||||
return class_ivar_set(obj, id, val, &dontcare);
|
||||
}
|
||||
@@ -2328,7 +2331,8 @@ rb_field_foreach(VALUE obj, rb_ivar_foreach_callback_func *func, st_data_t arg,
|
||||
case T_CLASS:
|
||||
case T_MODULE:
|
||||
{
|
||||
IVAR_ACCESSOR_SHOULD_BE_MAIN_RACTOR(0);
|
||||
// No owner check: every caller of this walk uses the names only for a
|
||||
// class/module. Values are checked where they are read (rb_ivar_lookup).
|
||||
VALUE fields_obj = RCLASS_WRITABLE_FIELDS_OBJ(obj);
|
||||
if (fields_obj) {
|
||||
imemo_fields_each(fields_obj, func, arg, ivar_only);
|
||||
@@ -2891,6 +2895,8 @@ rb_autoload_str(VALUE module, ID name, VALUE feature)
|
||||
rb_raise(rb_eNameError, "autoload must be constant name: %"PRIsVALUE"", QUOTE_ID(name));
|
||||
}
|
||||
|
||||
rb_class_owner_check(module);
|
||||
|
||||
Check_Type(feature, T_STRING);
|
||||
if (!RSTRING_LEN(feature)) {
|
||||
rb_raise(rb_eArgError, "empty feature name");
|
||||
@@ -3332,9 +3338,9 @@ rb_const_get_0(VALUE klass, ID id, int exclude, int recurse, int visibility)
|
||||
VALUE found_in;
|
||||
VALUE c = rb_const_search(klass, id, exclude, recurse, visibility, &found_in);
|
||||
if (!UNDEF_P(c)) {
|
||||
if (UNLIKELY(!rb_ractor_main_p())) {
|
||||
if (UNLIKELY(!rb_class_owned_p(found_in))) {
|
||||
if (!rb_ractor_shareable_p(c)) {
|
||||
rb_raise(rb_eRactorIsolationError, "can not access non-shareable objects in constant %"PRIsVALUE"::%"PRIsVALUE" by non-main Ractor.", rb_class_path(found_in), rb_id2str(id));
|
||||
rb_raise(rb_eRactorIsolationError, "can not access non-shareable objects in constant %"PRIsVALUE"::%"PRIsVALUE" of a class/module created by another Ractor.", rb_class_path(found_in), rb_id2str(id));
|
||||
}
|
||||
}
|
||||
return c;
|
||||
@@ -3546,6 +3552,7 @@ rb_const_remove(VALUE mod, ID id)
|
||||
rb_const_entry_t *ce;
|
||||
|
||||
rb_check_frozen(mod);
|
||||
rb_class_owner_check(mod);
|
||||
|
||||
ce = rb_const_lookup(mod, id);
|
||||
|
||||
@@ -3842,8 +3849,8 @@ const_set(VALUE klass, ID id, VALUE val)
|
||||
QUOTE_ID(id));
|
||||
}
|
||||
|
||||
if (!rb_ractor_main_p() && !rb_ractor_shareable_p(val)) {
|
||||
rb_raise(rb_eRactorIsolationError, "can not set constants with non-shareable objects by non-main Ractors");
|
||||
if (UNLIKELY(!rb_class_owned_p(klass))) {
|
||||
rb_raise(rb_eRactorIsolationError, "can not set constants of classes/modules created by another Ractor");
|
||||
}
|
||||
|
||||
check_before_mod_set(klass, id, val, "constant");
|
||||
|
||||
Reference in new issue
Block a user