summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authornormal <normal@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2018-05-10 04:18:28 +0000
committernormal <normal@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2018-05-10 04:18:28 +0000
commit6726038d761cbe3b4ac786de102e5c498c068b4e (patch)
tree408b201bd33d5660d1f80d08c3326b3447df434e
parent23c74845eda6cab10ac3b5e1b98e75fc73bfde75 (diff)
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.rb12
-rw-r--r--variable.c13
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);