Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 928260c

Browse files
chrisseatonnobu
authored andcommitted
Warn in verbose mode on defining a finalizer that captures the object
[Feature #15974] Closes: #2264
1 parent efde19c commit 928260c

File tree

2 files changed

+69
-0
lines changed

2 files changed

+69
-0
lines changed

gc.c

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2954,6 +2954,42 @@ should_be_finalizable(VALUE obj)
29542954
rb_check_frozen(obj);
29552955
}
29562956

2957+
struct should_not_capture_data {
2958+
VALUE obj;
2959+
VALUE set;
2960+
bool found;
2961+
};
2962+
2963+
static void
2964+
should_not_capture_callback(const VALUE child, struct should_not_capture_data *data)
2965+
{
2966+
if (child == data->obj)
2967+
data->found = true;
2968+
2969+
if (data->found)
2970+
return;
2971+
2972+
// Maintain a set of objects already searched, so that we don't follow a cycle
2973+
VALUE key = rb_obj_id(child);
2974+
if (rb_hash_has_key(data->set, key))
2975+
return;
2976+
rb_hash_aset(data->set, key, Qtrue);
2977+
2978+
rb_objspace_reachable_objects_from(child, (void (*)(unsigned long, void *)) &should_not_capture_callback, (void *)data);
2979+
}
2980+
2981+
static void
2982+
should_not_capture(VALUE block, VALUE obj)
2983+
{
2984+
struct should_not_capture_data data;
2985+
data.obj = obj;
2986+
data.set = rb_hash_new();
2987+
data.found = false;
2988+
rb_objspace_reachable_objects_from(block, (void (*)(unsigned long, void *)) &should_not_capture_callback, (void *)&data);
2989+
if (data.found)
2990+
rb_warn("object is reachable from finalizer - it may never be run");
2991+
}
2992+
29572993
/*
29582994
* call-seq:
29592995
* ObjectSpace.define_finalizer(obj, aProc=proc())
@@ -2963,6 +2999,10 @@ should_be_finalizable(VALUE obj)
29632999
* as an argument to <i>aProc</i>. If <i>aProc</i> is a lambda or
29643000
* method, make sure it can be called with a single argument.
29653001
*
3002+
* In verbose mode (<code>-w</code>) a warning will be issued if
3003+
* the object is reachable from <i>aProc</i>, which may prevent
3004+
* finalization.
3005+
*
29663006
*/
29673007

29683008
static VALUE
@@ -2979,6 +3019,9 @@ define_final(int argc, VALUE *argv, VALUE os)
29793019
should_be_callable(block);
29803020
}
29813021

3022+
if (ruby_verbose)
3023+
should_not_capture(block, obj);
3024+
29823025
return define_final0(obj, block);
29833026
}
29843027

spec/ruby/core/objectspace/define_finalizer_spec.rb

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,4 +65,30 @@ def scoped
6565

6666
ruby_exe(code).lines.sort.should == ["finalized1\n", "finalized2\n"]
6767
end
68+
69+
it "warns in verbose mode if it is self-referencing" do
70+
code = <<-RUBY
71+
obj = "Test"
72+
handler = Proc.new { puts "finalized" }
73+
ObjectSpace.define_finalizer(obj, handler)
74+
exit 0
75+
RUBY
76+
77+
ruby_exe(code, :options => "-w", :args => "2>&1").should include("warning: object is reachable from finalizer - it may never be run")
78+
end
79+
80+
it "warns in verbose mode if it is indirectly self-referencing" do
81+
code = <<-RUBY
82+
def scoped(indirect)
83+
Proc.new { puts "finalized" }
84+
end
85+
obj = "Test"
86+
indirect = [obj]
87+
handler = scoped(indirect)
88+
ObjectSpace.define_finalizer(obj, handler)
89+
exit 0
90+
RUBY
91+
92+
ruby_exe(code, :options => "-w", :args => "2>&1").should include("warning: object is reachable from finalizer - it may never be run")
93+
end
6894
end

0 commit comments

Comments
 (0)