Skip to content

Commit 6bb9bb8

Browse files
committed
Ractor: don't invoke initialize_* callbacks when moving an object
[Bug #20255] Alternatively we could invoke it, but would need to do it in the context of the newly spawned Ractor so that accessing global non-shareable objects is not possible.
1 parent 7db0e07 commit 6bb9bb8

5 files changed

Lines changed: 52 additions & 14 deletions

File tree

internal/object.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@ VALUE rb_class_search_ancestor(VALUE klass, VALUE super);
1717
NORETURN(void rb_undefined_alloc(VALUE klass));
1818
double rb_num_to_dbl(VALUE val);
1919
VALUE rb_obj_dig(int argc, VALUE *argv, VALUE self, VALUE notfound);
20-
VALUE rb_obj_clone_setup(VALUE obj, VALUE clone, VALUE kwfreeze);
20+
VALUE rb_obj_raw_clone(VALUE obj);
21+
VALUE rb_obj_clone_setup(VALUE obj, VALUE clone, VALUE kwfreeze, int init_callbacks);
2122
VALUE rb_obj_dup_setup(VALUE obj, VALUE dup);
2223
VALUE rb_immutable_obj_clone(int, VALUE *, VALUE);
2324
VALUE rb_check_convert_type_with_id(VALUE,int,const char*,ID);

object.c

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -404,7 +404,7 @@ init_copy(VALUE dest, VALUE obj)
404404
}
405405

406406
static VALUE immutable_obj_clone(VALUE obj, VALUE kwfreeze);
407-
static VALUE mutable_obj_clone(VALUE obj, VALUE kwfreeze);
407+
static VALUE mutable_obj_clone(VALUE obj, VALUE kwfreeze, int init_callbacks);
408408
PUREFUNC(static inline int special_object_p(VALUE obj)); /*!< \private */
409409
static inline int
410410
special_object_p(VALUE obj)
@@ -443,7 +443,7 @@ rb_obj_clone2(rb_execution_context_t *ec, VALUE obj, VALUE freeze)
443443
{
444444
VALUE kwfreeze = obj_freeze_opt(freeze);
445445
if (!special_object_p(obj))
446-
return mutable_obj_clone(obj, kwfreeze);
446+
return mutable_obj_clone(obj, kwfreeze, true);
447447
return immutable_obj_clone(obj, kwfreeze);
448448
}
449449

@@ -484,7 +484,7 @@ immutable_obj_clone(VALUE obj, VALUE kwfreeze)
484484
}
485485

486486
VALUE
487-
rb_obj_clone_setup(VALUE obj, VALUE clone, VALUE kwfreeze)
487+
rb_obj_clone_setup(VALUE obj, VALUE clone, VALUE kwfreeze, int init_callbacks)
488488
{
489489
VALUE argv[2];
490490

@@ -498,7 +498,9 @@ rb_obj_clone_setup(VALUE obj, VALUE clone, VALUE kwfreeze)
498498

499499
switch (kwfreeze) {
500500
case Qnil:
501-
rb_funcall(clone, id_init_clone, 1, obj);
501+
if (init_callbacks) {
502+
rb_funcall(clone, id_init_clone, 1, obj);
503+
}
502504
RBASIC(clone)->flags |= RBASIC(obj)->flags & FL_FREEZE;
503505

504506
if (RB_TYPE_P(obj, T_STRING)) {
@@ -526,7 +528,9 @@ rb_obj_clone_setup(VALUE obj, VALUE clone, VALUE kwfreeze)
526528

527529
argv[0] = obj;
528530
argv[1] = freeze_true_hash;
529-
rb_funcallv_kw(clone, id_init_clone, 2, argv, RB_PASS_KEYWORDS);
531+
if (init_callbacks) {
532+
rb_funcallv_kw(clone, id_init_clone, 2, argv, RB_PASS_KEYWORDS);
533+
}
530534
RBASIC(clone)->flags |= FL_FREEZE;
531535
rb_shape_t * next_shape = rb_shape_transition_shape_frozen(clone);
532536
// If we're out of shapes, but we want to freeze, then we need to
@@ -550,7 +554,9 @@ rb_obj_clone_setup(VALUE obj, VALUE clone, VALUE kwfreeze)
550554

551555
argv[0] = obj;
552556
argv[1] = freeze_false_hash;
553-
rb_funcallv_kw(clone, id_init_clone, 2, argv, RB_PASS_KEYWORDS);
557+
if (init_callbacks) {
558+
rb_funcallv_kw(clone, id_init_clone, 2, argv, RB_PASS_KEYWORDS);
559+
}
554560
break;
555561
}
556562
default:
@@ -561,17 +567,24 @@ rb_obj_clone_setup(VALUE obj, VALUE clone, VALUE kwfreeze)
561567
}
562568

563569
static VALUE
564-
mutable_obj_clone(VALUE obj, VALUE kwfreeze)
570+
mutable_obj_clone(VALUE obj, VALUE kwfreeze, int init_callbacks)
565571
{
566572
VALUE clone = rb_obj_alloc(rb_obj_class(obj));
567-
return rb_obj_clone_setup(obj, clone, kwfreeze);
573+
return rb_obj_clone_setup(obj, clone, kwfreeze, init_callbacks);
568574
}
569575

570576
VALUE
571577
rb_obj_clone(VALUE obj)
572578
{
573579
if (special_object_p(obj)) return obj;
574-
return mutable_obj_clone(obj, Qnil);
580+
return mutable_obj_clone(obj, Qnil, true);
581+
}
582+
583+
VALUE
584+
rb_obj_raw_clone(VALUE obj)
585+
{
586+
if (special_object_p(obj)) return obj;
587+
return mutable_obj_clone(obj, Qnil, false);
575588
}
576589

577590
VALUE

proc.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ static VALUE
126126
proc_clone(VALUE self)
127127
{
128128
VALUE procval = rb_proc_dup(self);
129-
return rb_obj_clone_setup(self, procval, Qnil);
129+
return rb_obj_clone_setup(self, procval, Qnil, true);
130130
}
131131

132132
/* :nodoc: */
@@ -317,7 +317,7 @@ static VALUE
317317
binding_clone(VALUE self)
318318
{
319319
VALUE bindval = binding_dup(self);
320-
return rb_obj_clone_setup(self, bindval, Qnil);
320+
return rb_obj_clone_setup(self, bindval, Qnil, true);
321321
}
322322

323323
VALUE
@@ -2452,7 +2452,7 @@ method_clone(VALUE self)
24522452

24532453
TypedData_Get_Struct(self, struct METHOD, &method_data_type, orig);
24542454
clone = TypedData_Make_Struct(CLASS_OF(self), struct METHOD, &method_data_type, data);
2455-
rb_obj_clone_setup(self, clone, Qnil);
2455+
rb_obj_clone_setup(self, clone, Qnil, true);
24562456
RB_OBJ_WRITE(clone, &data->recv, orig->recv);
24572457
RB_OBJ_WRITE(clone, &data->klass, orig->klass);
24582458
RB_OBJ_WRITE(clone, &data->iclass, orig->iclass);

ractor.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3555,7 +3555,7 @@ move_enter(VALUE obj, struct obj_traverse_replace_data *data)
35553555
return traverse_skip;
35563556
}
35573557
else {
3558-
data->replacement = rb_obj_clone(obj);
3558+
data->replacement = rb_obj_raw_clone(obj);
35593559
return traverse_cont;
35603560
}
35613561
}

test/ruby/test_ractor.rb

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,30 @@ def test_default_thread_group
7171
end;
7272
end
7373

74+
def test_initialize_clone_on_send
75+
assert_separately([], "#{<<~"begin;"}\n#{<<~'end;'}")
76+
begin;
77+
Warning[:experimental] = false
78+
79+
class A
80+
def initialize_clone(other)
81+
raise NotImplementedError
82+
end
83+
end
84+
85+
class B
86+
def initialize_copy(other)
87+
raise NotImplementedError
88+
end
89+
end
90+
91+
ractor = Ractor.new { 2.times { Ractor.receive} }
92+
ractor.send(A.new, move: true)
93+
ractor.send(B.new, move: true)
94+
ractor.take
95+
end;
96+
end
97+
7498
def assert_make_shareable(obj)
7599
refute Ractor.shareable?(obj), "object was already shareable"
76100
Ractor.make_shareable(obj)

0 commit comments

Comments
 (0)