diff options
author | Chris Seaton <chris@chrisseaton.com> | 2019-07-02 14:19:15 +0100 |
---|---|---|
committer | Nobuyoshi Nakada <nobu@ruby-lang.org> | 2019-07-03 04:05:22 +0900 |
commit | 928260c2a613bbdd4402c300e0bf86ae7562e52a (patch) | |
tree | 00e025542b7ae654ea1bc2dd249851f7d912f9b7 /gc.c | |
parent | efde19ce440f8656c3ce631a1d2a56e830961e9d (diff) | |
download | ruby-928260c2a613bbdd4402c300e0bf86ae7562e52a.tar.gz |
Warn in verbose mode on defining a finalizer that captures the object
[Feature #15974]
Closes: https://github.com/ruby/ruby/pull/2264
Diffstat (limited to 'gc.c')
-rw-r--r-- | gc.c | 43 |
1 files changed, 43 insertions, 0 deletions
@@ -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); } |