diff options
-rw-r--r-- | ChangeLog | 7 | ||||
-rw-r--r-- | hash.c | 39 | ||||
-rw-r--r-- | test/ruby/test_hash.rb | 28 |
3 files changed, 47 insertions, 27 deletions
@@ -1,3 +1,10 @@ +Mon Dec 2 21:49:19 2013 Masaki Matsushita <glass.saga@gmail.com> + + * 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 <charliesome@ruby-lang.org> * variable.c (rb_mod_constants): when calling Module#constants with @@ -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; - - 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; - } + 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; + + 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 |