summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorglass <glass@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2013-12-02 12:59:31 +0000
committerglass <glass@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2013-12-02 12:59:31 +0000
commitead0c5d356dd84acf3ad8a7f41f6b5bf2fb363c5 (patch)
tree5b65804464e28e99810adc0354872ec4d796708c
parent39a8519a569bd2190422c4a340950ccd5bbc7f04 (diff)
* 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
-rw-r--r--ChangeLog7
-rw-r--r--hash.c39
-rw-r--r--test/ruby/test_hash.rb28
3 files changed, 47 insertions, 27 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 <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
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;
-
- 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