Skip to content

Commit 085cc6e

Browse files
committed
Ractor: revert to moving object bytes, but size pool aware
Using `rb_obj_clone` introduce other problems, such as `initialize_*` callbacks invocation in the context of the parent ractor. So we can revert back to copy the content of the object slots, but in a way that is aware of size pools.
1 parent eb76591 commit 085cc6e

File tree

5 files changed

+73
-11
lines changed

5 files changed

+73
-11
lines changed

bootstraptest/test_ractor.rb

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2101,3 +2101,24 @@ def ==(o)
21012101
:fail
21022102
end
21032103
}
2104+
2105+
# move objects inside frozen containers
2106+
assert_equal 'ok', %q{
2107+
ractor = Ractor.new { Ractor.receive }
2108+
obj = Array.new(10, 42)
2109+
original = obj.dup
2110+
ractor.send([obj].freeze, move: true)
2111+
roundtripped_obj = ractor.take[0]
2112+
roundtripped_obj == original ? :ok : roundtripped_obj
2113+
}
2114+
2115+
# move object with generic ivar
2116+
assert_equal 'ok', %q{
2117+
ractor = Ractor.new { Ractor.receive }
2118+
obj = Array.new(10, 42)
2119+
obj.instance_variable_set(:@array, [1])
2120+
2121+
ractor.send(obj, move: true)
2122+
roundtripped_obj = ractor.take
2123+
roundtripped_obj.instance_variable_get(:@array) == [1] ? :ok : roundtripped_obj
2124+
}

gc.c

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2658,14 +2658,6 @@ rb_gc_mark_roots(void *objspace, const char **categoryp)
26582658

26592659
#define TYPED_DATA_REFS_OFFSET_LIST(d) (size_t *)(uintptr_t)RTYPEDDATA(d)->type->function.dmark
26602660

2661-
void
2662-
rb_gc_ractor_moved(VALUE dest, VALUE src)
2663-
{
2664-
rb_gc_obj_free(rb_gc_get_objspace(), src);
2665-
MEMZERO((void *)src, char, rb_gc_obj_slot_size(src));
2666-
RBASIC(src)->flags = T_OBJECT | FL_FREEZE; // Avoid mutations using bind_call, etc.
2667-
}
2668-
26692661
void
26702662
rb_gc_mark_children(void *objspace, VALUE obj)
26712663
{

internal/gc.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -183,7 +183,6 @@ struct rb_gc_object_metadata_entry {
183183
/* gc.c */
184184
RUBY_ATTR_MALLOC void *ruby_mimmalloc(size_t size);
185185
RUBY_ATTR_MALLOC void *ruby_mimcalloc(size_t num, size_t size);
186-
void rb_gc_ractor_moved(VALUE dest, VALUE src);
187186
void ruby_mimfree(void *ptr);
188187
void rb_gc_prepare_heap(void);
189188
void rb_objspace_set_event_hook(const rb_event_flag_t event);

ractor.c

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3539,6 +3539,19 @@ rb_obj_traverse_replace(VALUE obj,
35393539
}
35403540
}
35413541

3542+
static const bool wb_protected_types[RUBY_T_MASK] = {
3543+
[T_OBJECT] = RGENGC_WB_PROTECTED_OBJECT,
3544+
[T_HASH] = RGENGC_WB_PROTECTED_HASH,
3545+
[T_ARRAY] = RGENGC_WB_PROTECTED_ARRAY,
3546+
[T_STRING] = RGENGC_WB_PROTECTED_STRING,
3547+
[T_STRUCT] = RGENGC_WB_PROTECTED_STRUCT,
3548+
[T_COMPLEX] = RGENGC_WB_PROTECTED_COMPLEX,
3549+
[T_REGEXP] = RGENGC_WB_PROTECTED_REGEXP,
3550+
[T_MATCH] = RGENGC_WB_PROTECTED_MATCH,
3551+
[T_FLOAT] = RGENGC_WB_PROTECTED_FLOAT,
3552+
[T_RATIONAL] = RGENGC_WB_PROTECTED_RATIONAL,
3553+
};
3554+
35423555
static enum obj_traverse_iterator_result
35433556
move_enter(VALUE obj, struct obj_traverse_replace_data *data)
35443557
{
@@ -3547,15 +3560,31 @@ move_enter(VALUE obj, struct obj_traverse_replace_data *data)
35473560
return traverse_skip;
35483561
}
35493562
else {
3550-
data->replacement = rb_obj_clone(obj);
3563+
VALUE type = RB_BUILTIN_TYPE(obj);
3564+
type |= wb_protected_types[type] ? FL_WB_PROTECTED : 0;
3565+
NEWOBJ_OF(moved, struct RBasic, 0, type, rb_gc_obj_slot_size(obj), 0);
3566+
data->replacement = (VALUE)moved;
35513567
return traverse_cont;
35523568
}
35533569
}
35543570

35553571
static enum obj_traverse_iterator_result
35563572
move_leave(VALUE obj, struct obj_traverse_replace_data *data)
35573573
{
3558-
rb_gc_ractor_moved(data->replacement, obj);
3574+
size_t size = rb_gc_obj_slot_size(obj);
3575+
memcpy((void *)data->replacement, (void *)obj, size);
3576+
FL_UNSET_RAW(data->replacement, FL_SEEN_OBJ_ID);
3577+
3578+
void rb_replace_generic_ivar(VALUE clone, VALUE obj); // variable.c
3579+
3580+
if (UNLIKELY(FL_TEST_RAW(obj, FL_EXIVAR))) {
3581+
rb_replace_generic_ivar(data->replacement, obj);
3582+
}
3583+
3584+
// Avoid mutations using bind_call, etc.
3585+
// We keep FL_SEEN_OBJ_ID so GC later clean the obj_id_table.
3586+
MEMZERO((char *)obj + sizeof(struct RBasic), char, size - sizeof(struct RBasic));
3587+
RBASIC(obj)->flags = T_OBJECT | FL_FREEZE | (RBASIC(obj)->flags & FL_SEEN_OBJ_ID);
35593588
RBASIC_SET_CLASS_RAW(obj, rb_cRactorMovedObject);
35603589
return traverse_cont;
35613590
}

variable.c

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2157,6 +2157,27 @@ rb_copy_generic_ivar(VALUE clone, VALUE obj)
21572157
}
21582158
}
21592159

2160+
void
2161+
rb_replace_generic_ivar(VALUE clone, VALUE obj)
2162+
{
2163+
RUBY_ASSERT(FL_TEST(obj, FL_EXIVAR));
2164+
2165+
RB_VM_LOCK_ENTER();
2166+
{
2167+
st_data_t ivtbl, obj_data = (st_data_t)obj;
2168+
if (st_delete(generic_iv_tbl_, &obj_data, &ivtbl)) {
2169+
FL_UNSET_RAW(obj, FL_EXIVAR);
2170+
2171+
st_insert(generic_iv_tbl_, (st_data_t)clone, ivtbl);
2172+
FL_SET_RAW(clone, FL_EXIVAR);
2173+
}
2174+
else {
2175+
rb_bug("unreachable");
2176+
}
2177+
}
2178+
RB_VM_LOCK_LEAVE();
2179+
}
2180+
21602181
void
21612182
rb_ivar_foreach(VALUE obj, rb_ivar_foreach_callback_func *func, st_data_t arg)
21622183
{

0 commit comments

Comments
 (0)