зеркало из https://github.com/github/ruby.git
* 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:
Родитель
39a8519a56
Коммит
ead0c5d356
|
@ -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
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
|
||||
|
|
Загрузка…
Ссылка в новой задаче