From ead0c5d356dd84acf3ad8a7f41f6b5bf2fb363c5 Mon Sep 17 00:00:00 2001 From: glass Date: Mon, 2 Dec 2013 12:59:31 +0000 Subject: [PATCH] * hash.c (rb_hash_rehash): make temporary st_table under the control of GC. [Bug #9187] * test/ruby/test_hash.rb: add a test for above. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@43957 b2dd03c8-39d4-4d8f-98ff-823fe69b080e --- ChangeLog | 7 +++++++ hash.c | 37 +++++++++++-------------------------- test/ruby/test_hash.rb | 28 ++++++++++++++++++++++++++++ 3 files changed, 46 insertions(+), 26 deletions(-) diff --git a/ChangeLog b/ChangeLog index 4949f5669e..9233682688 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,10 @@ +Mon Dec 2 21:49:19 2013 Masaki Matsushita + + * hash.c (rb_hash_rehash): make temporary st_table under the control + of GC. [Bug #9187] + + * test/ruby/test_hash.rb: add a test for above. + Mon Dec 2 17:23:00 2013 Charlie Somerville * variable.c (rb_mod_constants): when calling Module#constants with diff --git a/hash.c b/hash.c index 8ceae61d29..6b5fca7e41 100644 --- a/hash.c +++ b/hash.c @@ -590,14 +590,6 @@ rb_hash_rehash_i(VALUE key, VALUE value, VALUE arg) return ST_CONTINUE; } -static VALUE -rehash_func(VALUE arg) -{ - struct rehash_arg *p = (struct rehash_arg *)arg; - rb_hash_foreach(p->hash, rb_hash_rehash_i, (VALUE)p->tbl); - return Qnil; -} - /* * call-seq: * hsh.rehash -> hsh @@ -621,30 +613,23 @@ rehash_func(VALUE arg) static VALUE rb_hash_rehash(VALUE hash) { - int state; - struct rehash_arg arg; - st_table *new_tbl, *old_tbl = RHASH(hash)->ntbl; + VALUE tmp; + st_table *tbl; if (RHASH_ITER_LEV(hash) > 0) { rb_raise(rb_eRuntimeError, "rehash during iteration"); } rb_hash_modify_check(hash); - if (!old_tbl) return hash; + if (!RHASH(hash)->ntbl) + return hash; + tmp = rb_hash_new(); + tbl = st_init_table_with_size(RHASH(hash)->ntbl->type, RHASH(hash)->ntbl->num_entries); + RHASH(tmp)->ntbl = tbl; - new_tbl = st_init_table_with_size(old_tbl->type, old_tbl->num_entries); - arg.hash = hash; - arg.tbl = new_tbl; - - rb_protect(rehash_func, (VALUE)&arg, &state); - - if (state) { - st_free_table(new_tbl); - rb_jump_tag(state); - } - else { - st_free_table(RHASH(hash)->ntbl); - RHASH(hash)->ntbl = new_tbl; - } + rb_hash_foreach(hash, rb_hash_rehash_i, (VALUE)tbl); + st_free_table(RHASH(hash)->ntbl); + RHASH(hash)->ntbl = tbl; + RHASH(tmp)->ntbl = 0; return hash; } diff --git a/test/ruby/test_hash.rb b/test/ruby/test_hash.rb index cfb61b6480..656b026f4a 100644 --- a/test/ruby/test_hash.rb +++ b/test/ruby/test_hash.rb @@ -1080,6 +1080,34 @@ class TestHash < Test::Unit::TestCase assert_not_equal([a,"hello"].hash, [b,"world"].hash, bug9151) end + def test_exception_in_rehash + bug9187 = '[ruby-core:58728] [Bug #9187]' + + prepare = <<-EOS + class Foo + def initialize + @raise = false + end + + def hash + raise if @raise + @raise = true + return 0 + end + end + EOS + + code = <<-EOS + h = {Foo.new => true} + 10_0000.times do + h.rehash rescue nil + end + GC.start + EOS + + assert_no_memory_leak([], prepare, code, bug9187) + end + class TestSubHash < TestHash class SubHash < Hash end