* 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
This commit is contained in:
glass 2013-12-02 12:59:31 +00:00
Родитель 39a8519a56
Коммит ead0c5d356
3 изменённых файлов: 46 добавлений и 26 удалений

Просмотреть файл

@ -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

37
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;
}

Просмотреть файл

@ -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