Skip to content

Commit fa783b5

Browse files
committed
Refactor rb_obj_evacuate_ivs_to_hash_table
That function is a bit too low level to called from multiple places. It's always used in tandem with `rb_shape_set_too_complex` and both have to know how the object is laid out to update the `iv_ptr`. So instead we can provide two higher level function: - `rb_obj_copy_ivs_to_hash_table` to prepare a `st_table` from an arbitrary oject. - `rb_obj_convert_to_too_complex` to assign the new `st_table` to the old object, and safely free the old `iv_ptr`. Unfortunately both can't be combined into one, because `rb_obj_copy_ivar` need `rb_obj_copy_ivs_to_hash_table` to copy from one object to another.
1 parent a6548e9 commit fa783b5

File tree

5 files changed

+57
-58
lines changed

5 files changed

+57
-58
lines changed

internal/variable.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,8 @@ VALUE rb_mod_set_temporary_name(VALUE, VALUE);
4747

4848
struct gen_ivtbl;
4949
int rb_gen_ivtbl_get(VALUE obj, ID id, struct gen_ivtbl **ivtbl);
50-
int rb_obj_evacuate_ivs_to_hash_table(ID key, VALUE val, st_data_t arg);
50+
void rb_obj_copy_ivs_to_hash_table(VALUE obj, st_table *table);
51+
void rb_obj_convert_to_too_complex(VALUE obj, st_table *table);
5152
void rb_evict_ivars_to_hash(VALUE obj);
5253

5354
RUBY_SYMBOL_EXPORT_BEGIN

object.c

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -293,12 +293,10 @@ rb_obj_copy_ivar(VALUE dest, VALUE obj)
293293
rb_shape_t * src_shape = rb_shape_get_shape(obj);
294294

295295
if (rb_shape_obj_too_complex(obj)) {
296+
// obj is TOO_COMPLEX so we can copy its iv_hash
296297
st_table * table = rb_st_init_numtable_with_size(rb_st_table_size(ROBJECT_IV_HASH(obj)));
297-
298-
rb_ivar_foreach(obj, rb_obj_evacuate_ivs_to_hash_table, (st_data_t)table);
299-
rb_shape_set_too_complex(dest);
300-
301-
ROBJECT(dest)->as.heap.ivptr = (VALUE *)table;
298+
st_replace(table, ROBJECT_IV_HASH(obj));
299+
rb_obj_convert_to_too_complex(dest, table);
302300

303301
return;
304302
}
@@ -328,10 +326,8 @@ rb_obj_copy_ivar(VALUE dest, VALUE obj)
328326
shape_to_set_on_dest = rb_shape_rebuild_shape(initial_shape, src_shape);
329327
if (UNLIKELY(rb_shape_id(shape_to_set_on_dest) == OBJ_TOO_COMPLEX_SHAPE_ID)) {
330328
st_table * table = rb_st_init_numtable_with_size(src_num_ivs);
331-
332-
rb_ivar_foreach(obj, rb_obj_evacuate_ivs_to_hash_table, (st_data_t)table);
333-
rb_shape_set_too_complex(dest);
334-
ROBJECT(dest)->as.heap.ivptr = (VALUE *)table;
329+
rb_obj_copy_ivs_to_hash_table(obj, table);
330+
rb_obj_convert_to_too_complex(dest, table);
335331

336332
return;
337333
}

shape.c

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -915,13 +915,6 @@ rb_shape_obj_too_complex(VALUE obj)
915915
return rb_shape_get_shape_id(obj) == OBJ_TOO_COMPLEX_SHAPE_ID;
916916
}
917917

918-
void
919-
rb_shape_set_too_complex(VALUE obj)
920-
{
921-
RUBY_ASSERT(!rb_shape_obj_too_complex(obj));
922-
rb_shape_set_shape_id(obj, OBJ_TOO_COMPLEX_SHAPE_ID);
923-
}
924-
925918
size_t
926919
rb_shape_edges_count(rb_shape_t *shape)
927920
{

shape.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -226,7 +226,6 @@ rb_shape_t *rb_shape_traverse_from_new_root(rb_shape_t *initial_shape, rb_shape_
226226
bool rb_shape_set_shape_id(VALUE obj, shape_id_t shape_id);
227227

228228
VALUE rb_obj_debug_shape(VALUE self, VALUE obj);
229-
void rb_shape_set_too_complex(VALUE obj);
230229

231230
// For ext/objspace
232231
RUBY_SYMBOL_EXPORT_BEGIN

variable.c

Lines changed: 50 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1371,55 +1371,59 @@ rb_attr_delete(VALUE obj, ID id)
13711371
}
13721372

13731373
void
1374-
rb_evict_ivars_to_hash(VALUE obj)
1374+
rb_obj_convert_to_too_complex(VALUE obj, st_table *table)
13751375
{
13761376
RUBY_ASSERT(!rb_shape_obj_too_complex(obj));
13771377

1378-
st_table *table = st_init_numtable_with_size(rb_ivar_count(obj));
1379-
1380-
// Evacuate all previous values from shape into id_table
1381-
rb_ivar_foreach(obj, rb_obj_evacuate_ivs_to_hash_table, (st_data_t)table);
1378+
VALUE *old_ivptr = NULL;
13821379

13831380
switch (BUILTIN_TYPE(obj)) {
1384-
case T_OBJECT:
1385-
rb_shape_set_too_complex(obj);
1386-
1387-
if (!(RBASIC(obj)->flags & ROBJECT_EMBED)) {
1388-
xfree(ROBJECT(obj)->as.heap.ivptr);
1389-
}
1390-
1391-
ROBJECT_SET_IV_HASH(obj, table);
1392-
break;
1393-
case T_CLASS:
1394-
case T_MODULE:
1395-
rb_shape_set_too_complex(obj);
1396-
1397-
xfree(RCLASS_IVPTR(obj));
1398-
RCLASS_SET_IV_HASH(obj, table);
1399-
break;
1400-
default:
1401-
RB_VM_LOCK_ENTER();
1402-
{
1403-
struct st_table *gen_ivs = generic_ivtbl_no_ractor_check(obj);
1404-
st_data_t old_ivtbl;
1405-
struct gen_ivtbl *ivtbl = NULL;
1406-
1407-
if (st_delete(gen_ivs, &obj, &old_ivtbl)) {
1408-
ivtbl = (struct gen_ivtbl *)old_ivtbl;
1409-
}
1410-
1411-
ivtbl = xrealloc(ivtbl, sizeof(struct gen_ivtbl));
1412-
ivtbl->as.complex.table = table;
1381+
case T_OBJECT:
1382+
if (!(RBASIC(obj)->flags & ROBJECT_EMBED)) {
1383+
old_ivptr = ROBJECT_IVPTR(obj);
1384+
}
1385+
rb_shape_set_shape_id(obj, OBJ_TOO_COMPLEX_SHAPE_ID);
1386+
ROBJECT_SET_IV_HASH(obj, table);
1387+
break;
1388+
case T_CLASS:
1389+
case T_MODULE:
1390+
old_ivptr = RCLASS_IVPTR(obj);
1391+
rb_shape_set_shape_id(obj, OBJ_TOO_COMPLEX_SHAPE_ID);
1392+
RCLASS_SET_IV_HASH(obj, table);
1393+
break;
1394+
default:
1395+
RB_VM_LOCK_ENTER();
1396+
{
1397+
struct st_table *gen_ivs = generic_ivtbl_no_ractor_check(obj);
1398+
st_lookup(gen_ivs, (st_data_t)&obj, (st_data_t *)&old_ivptr);
1399+
1400+
struct gen_ivtbl *ivtbl = xmalloc(sizeof(struct gen_ivtbl));
1401+
ivtbl->as.complex.table = table;
14131402
#if SHAPE_IN_BASIC_FLAGS
1414-
rb_shape_set_too_complex(obj);
1403+
rb_shape_set_shape_id(obj, OBJ_TOO_COMPLEX_SHAPE_ID);
14151404
#else
1416-
ivtbl->shape_id = OBJ_TOO_COMPLEX_SHAPE_ID;
1405+
ivtbl->shape_id = OBJ_TOO_COMPLEX_SHAPE_ID;
14171406
#endif
1407+
st_insert(gen_ivs, (st_data_t)obj, (st_data_t)ivtbl);
1408+
}
1409+
RB_VM_LOCK_LEAVE();
1410+
}
14181411

1419-
st_insert(gen_ivs, (st_data_t)obj, (st_data_t)ivtbl);
1420-
}
1421-
RB_VM_LOCK_LEAVE();
1412+
if (old_ivptr) {
1413+
xfree(old_ivptr);
14221414
}
1415+
}
1416+
1417+
void
1418+
rb_evict_ivars_to_hash(VALUE obj)
1419+
{
1420+
RUBY_ASSERT(!rb_shape_obj_too_complex(obj));
1421+
1422+
st_table *table = st_init_numtable_with_size(rb_ivar_count(obj));
1423+
1424+
// Evacuate all previous values from shape into id_table
1425+
rb_obj_copy_ivs_to_hash_table(obj, table);
1426+
rb_obj_convert_to_too_complex(obj, table);
14231427

14241428
RUBY_ASSERT(rb_shape_obj_too_complex(obj));
14251429
}
@@ -1637,12 +1641,18 @@ rb_ensure_iv_list_size(VALUE obj, uint32_t current_capacity, uint32_t new_capaci
16371641
}
16381642

16391643
int
1640-
rb_obj_evacuate_ivs_to_hash_table(ID key, VALUE val, st_data_t arg)
1644+
rb_obj_copy_ivs_to_hash_table_i(ID key, VALUE val, st_data_t arg)
16411645
{
16421646
st_insert((st_table *)arg, (st_data_t)key, (st_data_t)val);
16431647
return ST_CONTINUE;
16441648
}
16451649

1650+
void
1651+
rb_obj_copy_ivs_to_hash_table(VALUE obj, st_table *table)
1652+
{
1653+
rb_ivar_foreach(obj, rb_obj_copy_ivs_to_hash_table_i, (st_data_t)table);
1654+
}
1655+
16461656
static VALUE *
16471657
obj_ivar_set_shape_ivptr(VALUE obj, void *_data)
16481658
{

0 commit comments

Comments
 (0)