summaryrefslogtreecommitdiff
path: root/test/ruby
diff options
context:
space:
mode:
authornormal <normal@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2018-05-10 08:54:26 +0000
committernormal <normal@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2018-05-10 08:54:26 +0000
commitec959fbb4b90af84c7a0a0b405fbe15cc0933e0d (patch)
tree2b8ef80d99b357f8e2fac36ef5c35be612e00459 /test/ruby
parent5afa84b028ceed0669c0a52bd00e24f72509aabb (diff)
variable.c: fix multiple autoload with identical file (again)
We need to ensure autoload declarations pointing to the same feature (aka "file") can wait on each other to avoid deadlock situations. So, reorganize autoload data structures to maintain a feature => autoload_data_i mapping, and have module constant tables point to the new autoload_const struct instead of directly to autoload_data_i. This allows multiple autoload_const structs to refer to the SAME autoload_data_i struct, and with it, the on-stack autoload_state.waitq. The end result is different constants can share the same waitq (tied to the feature name), and not deadlock each other during loading. Thanks to Eugene Kenny for the bug report and reproducible test case. Reported-by: Eugene Kenny <elkenny@gmail.com> * variable.c (autoload_featuremap): new global (struct autoload_const): new per-const struct (struct autoload_state): reference autoload_const instead of autoload_data_i (struct autoload_data_i): remove per-const (autoload_i_mark): delete from autoload_featuremap if unreferenced (autoload_c_mark): new dmark callback (autoload_c_free): new dfree callback (autoload_c_memsize): new memsize callback (autoload_const_type): new data type (get_autoload_data): set autoload_const as well (rb_autoload_str): use new data structures (autoload_delete): cleanup from autoload_featuremap (check_autoload_required): adjust for new internals (rb_autoloading_value): ditto (struct autoload_const_set_args): remove, redundant with autoload_const (const_tbl_update): adjust for new internals (autoload_const_set): ditto (autoload_require): ditto (autoload_reset): ditto (rb_autoload_load): ditto (rb_const_set): ditto (current_autoload_data): ditto (set_const_visibility): ditto * test/ruby/test_autoload.rb (test_autoload_same_file): new test (test_no_leak): new test [ruby-core:86935] [Bug #14742] git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@63392 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
Diffstat (limited to 'test/ruby')
-rw-r--r--test/ruby/test_autoload.rb36
1 files changed, 36 insertions, 0 deletions
diff --git a/test/ruby/test_autoload.rb b/test/ruby/test_autoload.rb
index 3095052a81..8311c40c35 100644
--- a/test/ruby/test_autoload.rb
+++ b/test/ruby/test_autoload.rb
@@ -311,6 +311,42 @@ p Foo::Bar
end
end if Process.respond_to?(:fork)
+ def test_autoload_same_file
+ Dir.mktmpdir('autoload') do |tmpdir|
+ File.write("#{tmpdir}/b.rb", "#{<<~'begin;'}\n#{<<~'end;'}")
+ begin;
+ module Foo; end
+ module Bar; end
+ end;
+ 3.times do # timing-dependent, needs a few times to hit [Bug #14742]
+ assert_separately(%W[-I #{tmpdir}], "#{<<-'begin;'}\n#{<<-'end;'}")
+ begin;
+ autoload :Foo, 'b'
+ autoload :Bar, 'b'
+ t1 = Thread.new do Foo end
+ t2 = Thread.new do Bar end
+ t1.join
+ t2.join
+ bug = '[ruby-core:86935] [Bug #14742]'
+ assert_instance_of Module, t1.value, bug
+ assert_instance_of Module, t2.value, bug
+ end;
+ end
+ 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)}