1
0
Fork 0
mirror of https://github.com/ruby/ruby.git synced 2022-11-09 12:17:21 -05:00

Warn in verbose mode on defining a finalizer that captures the object

[Feature #15974]

Closes: https://github.com/ruby/ruby/pull/2264
This commit is contained in:
Chris Seaton 2019-07-02 14:19:15 +01:00 committed by Nobuyoshi Nakada
parent efde19ce44
commit 928260c2a6
No known key found for this signature in database
GPG key ID: 4BC7D6DF58D8DF60
2 changed files with 69 additions and 0 deletions

43
gc.c
View file

@ -2954,6 +2954,42 @@ should_be_finalizable(VALUE obj)
rb_check_frozen(obj);
}
struct should_not_capture_data {
VALUE obj;
VALUE set;
bool found;
};
static void
should_not_capture_callback(const VALUE child, struct should_not_capture_data *data)
{
if (child == data->obj)
data->found = true;
if (data->found)
return;
// Maintain a set of objects already searched, so that we don't follow a cycle
VALUE key = rb_obj_id(child);
if (rb_hash_has_key(data->set, key))
return;
rb_hash_aset(data->set, key, Qtrue);
rb_objspace_reachable_objects_from(child, (void (*)(unsigned long, void *)) &should_not_capture_callback, (void *)data);
}
static void
should_not_capture(VALUE block, VALUE obj)
{
struct should_not_capture_data data;
data.obj = obj;
data.set = rb_hash_new();
data.found = false;
rb_objspace_reachable_objects_from(block, (void (*)(unsigned long, void *)) &should_not_capture_callback, (void *)&data);
if (data.found)
rb_warn("object is reachable from finalizer - it may never be run");
}
/*
* call-seq:
* ObjectSpace.define_finalizer(obj, aProc=proc())
@ -2963,6 +2999,10 @@ should_be_finalizable(VALUE obj)
* as an argument to <i>aProc</i>. If <i>aProc</i> is a lambda or
* method, make sure it can be called with a single argument.
*
* In verbose mode (<code>-w</code>) a warning will be issued if
* the object is reachable from <i>aProc</i>, which may prevent
* finalization.
*
*/
static VALUE
@ -2979,6 +3019,9 @@ define_final(int argc, VALUE *argv, VALUE os)
should_be_callable(block);
}
if (ruby_verbose)
should_not_capture(block, obj);
return define_final0(obj, block);
}

View file

@ -65,4 +65,30 @@ describe "ObjectSpace.define_finalizer" do
ruby_exe(code).lines.sort.should == ["finalized1\n", "finalized2\n"]
end
it "warns in verbose mode if it is self-referencing" do
code = <<-RUBY
obj = "Test"
handler = Proc.new { puts "finalized" }
ObjectSpace.define_finalizer(obj, handler)
exit 0
RUBY
ruby_exe(code, :options => "-w", :args => "2>&1").should include("warning: object is reachable from finalizer - it may never be run")
end
it "warns in verbose mode if it is indirectly self-referencing" do
code = <<-RUBY
def scoped(indirect)
Proc.new { puts "finalized" }
end
obj = "Test"
indirect = [obj]
handler = scoped(indirect)
ObjectSpace.define_finalizer(obj, handler)
exit 0
RUBY
ruby_exe(code, :options => "-w", :args => "2>&1").should include("warning: object is reachable from finalizer - it may never be run")
end
end