summaryrefslogtreecommitdiff
path: root/variable.c
diff options
context:
space:
mode:
authorJean Boussier <[email protected]>2025-05-22 14:01:46 +0200
committerJean Boussier <[email protected]>2025-06-12 07:58:16 +0200
commit3abdd4241fd5231a5711ce1b087d660c667ef30d ()
treeddcdd184ca6720bac671cf296a5b7474a22477f5 /variable.c
parent166ff187bd2a84fddd7a633bdbdbcd4ae393c91e (diff)
Turn `rb_classext_t.fields` into a T_IMEMO/class_fields
This behave almost exactly as a T_OBJECT, the layout is entirely compatible. This aims to solve two problems. First, it solves the problem of namspaced classes having a single `shape_id`. Now each namespaced classext has an object that can hold the namespace specific shape. Second, it open the door to later make class instance variable writes atomics, hence be able to read class variables without locking the VM. In the future, in multi-ractor mode, we can do the write on a copy of the `fields_obj` and then atomically swap it. Considerations: - Right now the `RClass` shape_id is always synchronized, but with namespace we should likely mark classes that have multiple namespace with a specific shape flag.
Notes: Merged: https://.com/ruby/ruby/pull/13411
-rw-r--r--variable.c319
1 files changed, 210 insertions, 109 deletions
@@ -1305,13 +1305,21 @@ rb_obj_field_get(VALUE obj, shape_id_t target_shape_id)
RUBY_ASSERT(!SPECIAL_CONST_P(obj));
RUBY_ASSERT(RSHAPE_TYPE_P(target_shape_id, SHAPE_IVAR) || RSHAPE_TYPE_P(target_shape_id, SHAPE_OBJ_ID));
if (rb_shape_too_complex_p(target_shape_id)) {
st_table *fields_hash;
switch (BUILTIN_TYPE(obj)) {
case T_CLASS:
case T_MODULE:
- ASSERT_vm_locking();
- fields_hash = RCLASS_FIELDS_HASH(obj);
break;
case T_OBJECT:
fields_hash = ROBJECT_FIELDS_HASH(obj);
@@ -1342,8 +1350,7 @@ rb_obj_field_get(VALUE obj, shape_id_t target_shape_id)
switch (BUILTIN_TYPE(obj)) {
case T_CLASS:
case T_MODULE:
- ASSERT_vm_locking();
- fields = RCLASS_PRIME_FIELDS(obj);
break;
case T_OBJECT:
fields = ROBJECT_FIELDS(obj);
@@ -1364,6 +1371,27 @@ rb_ivar_lookup(VALUE obj, ID id, VALUE undef)
{
if (SPECIAL_CONST_P(obj)) return undef;
shape_id_t shape_id;
VALUE * ivar_list;
shape_id = RBASIC_SHAPE_ID(obj);
@@ -1372,43 +1400,27 @@ rb_ivar_lookup(VALUE obj, ID id, VALUE undef)
case T_CLASS:
case T_MODULE:
{
- bool found = false;
- VALUE val;
-
- RB_VM_LOCKING() {
- if (rb_shape_too_complex_p(shape_id)) {
- st_table * iv_table = RCLASS_FIELDS_HASH(obj);
- if (rb_st_lookup(iv_table, (st_data_t)id, (st_data_t *)&val)) {
- found = true;
- }
- else {
- val = undef;
- }
}
else {
- attr_index_t index = 0;
- found = rb_shape_get_iv_index(shape_id, id, &index);
-
- if (found) {
- ivar_list = RCLASS_PRIME_FIELDS(obj);
- RUBY_ASSERT(ivar_list);
-
- val = ivar_list[index];
- }
- else {
- val = undef;
- }
}
}
- if (found &&
- rb_is_instance_id(id) &&
- UNLIKELY(!rb_ractor_main_p()) &&
- !rb_ractor_shareable_p(val)) {
- rb_raise(rb_eRactorIsolationError,
- "can not get unshareable values from instance variables of classes/modules from non-main Ractors");
- }
- return val;
}
case T_OBJECT:
{
@@ -1476,13 +1488,19 @@ rb_ivar_delete(VALUE obj, ID id, VALUE undef)
{
rb_check_frozen(obj);
- bool locked = false;
- unsigned int lev = 0;
VALUE val = undef;
if (BUILTIN_TYPE(obj) == T_CLASS || BUILTIN_TYPE(obj) == T_MODULE) {
IVAR_ACCESSOR_SHOULD_BE_MAIN_RACTOR(id);
- RB_VM_LOCK_ENTER_LEV(&lev);
- locked = true;
}
shape_id_t old_shape_id = rb_obj_shape_id(obj);
@@ -1494,9 +1512,6 @@ rb_ivar_delete(VALUE obj, ID id, VALUE undef)
shape_id_t next_shape_id = rb_shape_transition_remove_ivar(obj, id, &removed_shape_id);
if (next_shape_id == old_shape_id) {
- if (locked) {
- RB_VM_LOCK_LEAVE_LEV(&lev);
- }
return undef;
}
@@ -1511,7 +1526,11 @@ rb_ivar_delete(VALUE obj, ID id, VALUE undef)
switch(BUILTIN_TYPE(obj)) {
case T_CLASS:
case T_MODULE:
- fields = RCLASS_PRIME_FIELDS(obj);
break;
case T_OBJECT:
fields = ROBJECT_FIELDS(obj);
@@ -1546,10 +1565,6 @@ rb_ivar_delete(VALUE obj, ID id, VALUE undef)
}
rb_obj_set_shape_id(obj, next_shape_id);
- if (locked) {
- RB_VM_LOCK_LEAVE_LEV(&lev);
- }
-
return val;
too_complex:
@@ -1558,7 +1573,12 @@ too_complex:
switch (BUILTIN_TYPE(obj)) {
case T_CLASS:
case T_MODULE:
- table = RCLASS_WRITABLE_FIELDS_HASH(obj);
break;
case T_OBJECT:
@@ -1581,10 +1601,6 @@ too_complex:
}
}
- if (locked) {
- RB_VM_LOCK_LEAVE_LEV(&lev);
- }
-
return val;
}
@@ -1597,6 +1613,11 @@ rb_attr_delete(VALUE obj, ID id)
static shape_id_t
obj_transition_too_complex(VALUE obj, st_table *table)
{
RUBY_ASSERT(!rb_shape_obj_too_complex_p(obj));
shape_id_t shape_id = rb_shape_transition_complex(obj);
@@ -1612,9 +1633,7 @@ obj_transition_too_complex(VALUE obj, st_table *table)
break;
case T_CLASS:
case T_MODULE:
- old_fields = RCLASS_PRIME_FIELDS(obj);
- RBASIC_SET_SHAPE_ID(obj, shape_id);
- RCLASS_SET_FIELDS_HASH(obj, table);
break;
default:
RB_VM_LOCKING() {
@@ -2035,11 +2054,20 @@ rb_vm_set_ivar_id(VALUE obj, ID id, VALUE val)
bool
rb_obj_set_shape_id(VALUE obj, shape_id_t shape_id)
{
- if (rb_obj_shape_id(obj) == shape_id) {
return false;
}
RBASIC_SET_SHAPE_ID(obj, shape_id);
return true;
}
@@ -2131,7 +2159,12 @@ ivar_defined0(VALUE obj, ID id)
switch (BUILTIN_TYPE(obj)) {
case T_CLASS:
case T_MODULE:
- table = (st_table *)RCLASS_FIELDS_HASH(obj);
break;
case T_OBJECT:
@@ -2163,12 +2196,15 @@ rb_ivar_defined(VALUE obj, ID id)
{
if (SPECIAL_CONST_P(obj)) return Qfalse;
- VALUE defined;
switch (BUILTIN_TYPE(obj)) {
case T_CLASS:
case T_MODULE:
RB_VM_LOCKING() {
- defined = ivar_defined0(obj, id);
}
break;
default:
@@ -2183,6 +2219,7 @@ struct iv_itr_data {
struct gen_fields_tbl *fields_tbl;
st_data_t arg;
rb_ivar_foreach_callback_func *func;
bool ivar_only;
};
@@ -2203,8 +2240,12 @@ iterate_over_shapes_callback(shape_id_t shape_id, void *data)
break;
case T_CLASS:
case T_MODULE:
RUBY_ASSERT(!rb_shape_obj_too_complex_p(itr_data->obj));
- iv_list = RCLASS_PRIME_FIELDS(itr_data->obj);
break;
default:
iv_list = itr_data->fields_tbl->as.shape.fields;
@@ -2247,6 +2288,7 @@ obj_fields_each(VALUE obj, rb_ivar_foreach_callback_func *func, st_data_t arg, b
rb_st_foreach(ROBJECT_FIELDS_HASH(obj), each_hash_iv, (st_data_t)&itr_data);
}
else {
iterate_over_shapes(shape_id, func, &itr_data);
}
}
@@ -2270,27 +2312,29 @@ gen_fields_each(VALUE obj, rb_ivar_foreach_callback_func *func, st_data_t arg, b
rb_st_foreach(fields_tbl->as.complex.table, each_hash_iv, (st_data_t)&itr_data);
}
else {
iterate_over_shapes(shape_id, func, &itr_data);
}
}
static void
-class_fields_each(VALUE obj, rb_ivar_foreach_callback_func *func, st_data_t arg, bool ivar_only)
{
- RUBY_ASSERT(RB_TYPE_P(obj, T_CLASS) || RB_TYPE_P(obj, T_MODULE));
struct iv_itr_data itr_data = {
- .obj = obj,
.arg = arg,
.func = func,
.ivar_only = ivar_only,
};
- shape_id_t shape_id = RBASIC_SHAPE_ID(obj);
if (rb_shape_too_complex_p(shape_id)) {
- rb_st_foreach(RCLASS_WRITABLE_FIELDS_HASH(obj), each_hash_iv, (st_data_t)&itr_data);
}
else {
iterate_over_shapes(shape_id, func, &itr_data);
}
}
@@ -2399,6 +2443,11 @@ rb_field_foreach(VALUE obj, rb_ivar_foreach_callback_func *func, st_data_t arg,
{
if (SPECIAL_CONST_P(obj)) return;
switch (BUILTIN_TYPE(obj)) {
case T_OBJECT:
obj_fields_each(obj, func, arg, ivar_only);
break;
@@ -2406,11 +2455,14 @@ rb_field_foreach(VALUE obj, rb_ivar_foreach_callback_func *func, st_data_t arg,
case T_MODULE:
IVAR_ACCESSOR_SHOULD_BE_MAIN_RACTOR(0);
RB_VM_LOCKING() {
- class_fields_each(obj, func, arg, ivar_only);
}
break;
default:
- if (FL_TEST(obj, FL_EXIVAR)) {
gen_fields_each(obj, func, arg, ivar_only);
}
break;
@@ -2435,8 +2487,16 @@ rb_ivar_count(VALUE obj)
break;
case T_CLASS:
case T_MODULE:
- iv_count = RCLASS_FIELDS_COUNT(obj);
- break;
default:
if (FL_TEST(obj, FL_EXIVAR)) {
struct gen_fields_tbl *fields_tbl;
@@ -4642,38 +4702,91 @@ rb_iv_set(VALUE obj, const char *name, VALUE val)
return rb_ivar_set(obj, id, val);
}
-static VALUE *
-class_ivar_set_shape_fields(VALUE obj, void *_data)
{
- RUBY_ASSERT(!rb_shape_obj_too_complex_p(obj));
- return RCLASS_PRIME_FIELDS(obj);
-}
-static void
-class_ivar_set_shape_resize_fields(VALUE obj, attr_index_t _old_capa, attr_index_t new_capa, void *_data)
-{
- REALLOC_N(RCLASS_PRIME_FIELDS(obj), VALUE, new_capa);
-}
-static void
-class_ivar_set_set_shape_id(VALUE obj, shape_id_t shape_id, void *_data)
-{
- rb_obj_set_shape_id(obj, shape_id);
-}
-static shape_id_t
-class_ivar_set_transition_too_complex(VALUE obj, void *_data)
-{
- return rb_evict_fields_to_hash(obj);
-}
-static st_table *
-class_ivar_set_too_complex_table(VALUE obj, void *_data)
-{
- RUBY_ASSERT(rb_shape_obj_too_complex_p(obj));
- return RCLASS_WRITABLE_FIELDS_HASH(obj);
}
int
@@ -4686,12 +4799,7 @@ rb_class_ivar_set(VALUE obj, ID id, VALUE val)
rb_class_ensure_writable(obj);
RB_VM_LOCKING() {
- existing = general_ivar_set(obj, id, val, NULL,
- class_ivar_set_shape_fields,
- class_ivar_set_shape_resize_fields,
- class_ivar_set_set_shape_id,
- class_ivar_set_transition_too_complex,
- class_ivar_set_too_complex_table).existing;
}
return existing;
@@ -4701,12 +4809,7 @@ static void
class_field_set(VALUE obj, shape_id_t target_shape_id, VALUE val)
{
RUBY_ASSERT(RB_TYPE_P(obj, T_CLASS) || RB_TYPE_P(obj, T_MODULE));
- general_field_set(obj, target_shape_id, val, NULL,
- class_ivar_set_shape_fields,
- class_ivar_set_shape_resize_fields,
- class_ivar_set_set_shape_id,
- class_ivar_set_transition_too_complex,
- class_ivar_set_too_complex_table);
}
static int
@@ -4722,9 +4825,7 @@ rb_fields_tbl_copy(VALUE dst, VALUE src)
{
RUBY_ASSERT(rb_type(dst) == rb_type(src));
RUBY_ASSERT(RB_TYPE_P(dst, T_CLASS) || RB_TYPE_P(dst, T_MODULE));
-
RUBY_ASSERT(RSHAPE_TYPE_P(RBASIC_SHAPE_ID(dst), SHAPE_ROOT));
- RUBY_ASSERT(!RCLASS_PRIME_FIELDS(dst));
rb_ivar_foreach(src, tbl_copy_i, dst);
}