diff options
author | normal <normal@b2dd03c8-39d4-4d8f-98ff-823fe69b080e> | 2018-05-10 04:18:28 +0000 |
---|---|---|
committer | normal <normal@b2dd03c8-39d4-4d8f-98ff-823fe69b080e> | 2018-05-10 04:18:28 +0000 |
commit | 6726038d761cbe3b4ac786de102e5c498c068b4e (patch) | |
tree | 408b201bd33d5660d1f80d08c3326b3447df434e | |
parent | 23c74845eda6cab10ac3b5e1b98e75fc73bfde75 (diff) | |
download | ruby-6726038d761cbe3b4ac786de102e5c498c068b4e.tar.gz |
variable.c: fix autoload object lifetimes and leak
We must not call normal Hash methods inside GC free callback,
either, however identity hash may be used.
[ruby-core:86935] [Bug #14742]
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@63389 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
-rw-r--r-- | test/ruby/test_autoload.rb | 12 | ||||
-rw-r--r-- | variable.c | 13 |
2 files changed, 19 insertions, 6 deletions
diff --git a/test/ruby/test_autoload.rb b/test/ruby/test_autoload.rb index 6d51c16927..8311c40c35 100644 --- a/test/ruby/test_autoload.rb +++ b/test/ruby/test_autoload.rb @@ -335,6 +335,18 @@ p Foo::Bar end end + def test_no_leak + assert_no_memory_leak([], '', <<~'end;', 'many autoloads', timeout: 30) + 200000.times do |i| + m = Module.new + m.instance_eval do + autoload :Foo, 'x' + autoload :Bar, i.to_s + end + end + end; + end + def add_autoload(path) (@autoload_paths ||= []) << path ::Object.class_eval {autoload(:AutoloadTest, path)} diff --git a/variable.c b/variable.c index 6590567bf9..2d6fd22a70 100644 --- a/variable.c +++ b/variable.c @@ -1908,6 +1908,7 @@ autoload_c_free(void *ptr) { struct autoload_const *ac = ptr; list_del(&ac->cnode); + xfree(ac); } static size_t @@ -1990,7 +1991,7 @@ rb_autoload_str(VALUE mod, ID id, VALUE file) } file = rb_fstring(file); if (!autoload_featuremap) { - autoload_featuremap = rb_hash_new(); + autoload_featuremap = rb_hash_new_compare_by_id(); rb_obj_hide(autoload_featuremap); rb_gc_register_mark_object(autoload_featuremap); } @@ -2036,13 +2037,13 @@ autoload_delete(VALUE mod, ID id) ele = get_autoload_data((VALUE)load, &ac); VM_ASSERT(!list_empty(&ele->constants)); - /* list_del_init to make list_del in autoload_c_free idempotent: */ + /* + * we must delete here to avoid "already initialized" warnings + * with parallel autoload. list_del_init makes list_del in + * autoload_c_free idempotent + */ list_del_init(&ac->cnode); - if (list_empty(&ele->constants)) { - rb_hash_delete(autoload_featuremap, ele->feature); - } - if (tbl->num_entries == 0) { n = autoload; st_delete(RCLASS_IV_TBL(mod), &n, &val); |