@@ -1844,6 +1844,9 @@ imemo_fields_set(VALUE klass, VALUE fields_obj, shape_id_t target_shape_id, ID f
18441844 if (UNLIKELY (rb_shape_too_complex_p (target_shape_id ))) {
18451845 if (rb_shape_too_complex_p (current_shape_id )) {
18461846 if (concurrent ) {
1847+ // In multi-ractor case, we must always work on a copy because
1848+ // even if the field already exist, inserting in a st_table may
1849+ // cause a rebuild.
18471850 fields_obj = rb_imemo_fields_clone (fields_obj );
18481851 }
18491852 }
@@ -4680,9 +4683,7 @@ class_fields_ivar_set(VALUE klass, VALUE fields_obj, ID id, VALUE val, bool conc
46804683 attr_index_t next_capacity = RSHAPE_CAPACITY (next_shape_id );
46814684 attr_index_t current_capacity = RSHAPE_CAPACITY (current_shape_id );
46824685
4683- if (concurrent || next_capacity != current_capacity ) {
4684- RUBY_ASSERT (concurrent || next_capacity > current_capacity );
4685-
4686+ if (next_capacity > current_capacity ) {
46864687 // We allocate a new fields_obj even when concurrency isn't a concern
46874688 // so that we're embedded as long as possible.
46884689 fields_obj = imemo_fields_copy_capa (rb_singleton_class (klass ), fields_obj , next_capacity );
@@ -4693,7 +4694,18 @@ class_fields_ivar_set(VALUE klass, VALUE fields_obj, ID id, VALUE val, bool conc
46934694 }
46944695
46954696 VALUE * fields = rb_imemo_fields_ptr (fields_obj );
4696- RB_OBJ_WRITE (fields_obj , & fields [index ], val );
4697+
4698+ if (concurrent && original_fields_obj == fields_obj ) {
4699+ // In the concurrent case, if we're mutating the existing
4700+ // fields_obj, we must use an atomic write, because if we're
4701+ // adding a new field, the shape_id must be written after the field
4702+ // and if we're updating an existing field, we at least need a relaxed
4703+ // write to avoid reaping.
4704+ RB_OBJ_ATOMIC_WRITE (fields_obj , & fields [index ], val );
4705+ }
4706+ else {
4707+ RB_OBJ_WRITE (fields_obj , & fields [index ], val );
4708+ }
46974709
46984710 if (!existing ) {
46994711 RBASIC_SET_SHAPE_ID (fields_obj , next_shape_id );
@@ -4705,9 +4717,12 @@ class_fields_ivar_set(VALUE klass, VALUE fields_obj, ID id, VALUE val, bool conc
47054717too_complex :
47064718 {
47074719 if (concurrent && fields_obj == original_fields_obj ) {
4708- // If we're in the multi-ractor mode, we can't directly insert in the table.
4720+ // In multi-ractor case, we must always work on a copy because
4721+ // even if the field already exist, inserting in a st_table may
4722+ // cause a rebuild.
47094723 fields_obj = rb_imemo_fields_clone (fields_obj );
47104724 }
4725+
47114726 st_table * table = rb_imemo_fields_complex_tbl (fields_obj );
47124727 existing = st_insert (table , (st_data_t )id , (st_data_t )val );
47134728 RB_OBJ_WRITTEN (fields_obj , Qundef , val );
0 commit comments