ruby-changes:51182
From: normal <ko1@a...>
Date: Thu, 10 May 2018 13:18:32 +0900 (JST)
Subject: [ruby-changes:51182] normal:r63389 (trunk): variable.c: fix autoload object lifetimes and leak
normal 2018-05-10 13:18:28 +0900 (Thu, 10 May 2018) New Revision: 63389 https://svn.ruby-lang.org/cgi-bin/viewvc.cgi?view=revision&revision=63389 Log: 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] Modified files: trunk/test/ruby/test_autoload.rb trunk/variable.c Index: test/ruby/test_autoload.rb =================================================================== --- test/ruby/test_autoload.rb (revision 63388) +++ test/ruby/test_autoload.rb (revision 63389) @@ -335,6 +335,18 @@ p Foo::Bar https://github.com/ruby/ruby/blob/trunk/test/ruby/test_autoload.rb#L335 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)} Index: variable.c =================================================================== --- variable.c (revision 63388) +++ variable.c (revision 63389) @@ -1908,6 +1908,7 @@ autoload_c_free(void *ptr) https://github.com/ruby/ruby/blob/trunk/variable.c#L1908 { 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 https://github.com/ruby/ruby/blob/trunk/variable.c#L1991 } 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) https://github.com/ruby/ruby/blob/trunk/variable.c#L2037 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); -- ML: ruby-changes@q... Info: http://www.atdot.net/~ko1/quickml/