diff options
author | nobu <nobu@b2dd03c8-39d4-4d8f-98ff-823fe69b080e> | 2013-11-15 13:21:38 +0000 |
---|---|---|
committer | nobu <nobu@b2dd03c8-39d4-4d8f-98ff-823fe69b080e> | 2013-11-15 13:21:38 +0000 |
commit | e7d29c6cdadbb08d39219a05d795fe6c38414e19 (patch) | |
tree | 158ca07b9a2bbe70ec90d308329f9cbd13fd55c7 | |
parent | ca5739979dc932e809cb70d243ecc07d380d2725 (diff) |
hash.c: iteration level with reentering
* hash.c (hash_foreach_iter, hash_foreach_ensure, rb_hash_foreach):
deal with iteration level when reentering by callcc. temporary
measure until rollback of ensure is introduced. [ruby-dev:47807]
[Bug #9105]
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@43683 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
-rw-r--r-- | ChangeLog | 7 | ||||
-rw-r--r-- | hash.c | 14 | ||||
-rw-r--r-- | test/ruby/test_hash.rb | 18 |
3 files changed, 36 insertions, 3 deletions
@@ -1,3 +1,10 @@ +Fri Nov 15 22:21:34 2013 Nobuyoshi Nakada <nobu@ruby-lang.org> + + * hash.c (hash_foreach_iter, hash_foreach_ensure, rb_hash_foreach): + deal with iteration level when reentering by callcc. temporary + measure until rollback of ensure is introduced. [ruby-dev:47807] + [Bug #9105] + Fri Nov 15 17:07:31 2013 Nobuyoshi Nakada <nobu@ruby-lang.org> * lib/delegate.rb (Delegator#send): override to get rid of global function interference. @@ -173,7 +173,7 @@ struct hash_foreach_arg { VALUE hash; rb_foreach_func *func; VALUE arg; - int iter_lev; + VALUE marker; }; static int @@ -189,6 +189,10 @@ hash_foreach_iter(st_data_t key, st_data_t value, st_data_t argp, int error) if (RHASH(arg->hash)->ntbl != tbl) { rb_raise(rb_eRuntimeError, "rehash occurred during iteration"); } + if (DATA_PTR(arg->marker)) { + RHASH_ITER_LEV(arg->hash)++; + DATA_PTR(arg->marker) = 0; + } switch (status) { case ST_DELETE: FL_SET(arg->hash, HASH_DELETED); @@ -207,7 +211,10 @@ hash_foreach_ensure(VALUE arg) struct hash_foreach_arg *argp = (struct hash_foreach_arg *)arg; VALUE hash = argp->hash; - if ((RHASH_ITER_LEV(hash) = argp->iter_lev) == 0) { + if (DATA_PTR(argp->marker)) return 0; + DATA_PTR(argp->marker) = (void *)-1; + + if (--RHASH_ITER_LEV(hash) == 0) { if (FL_TEST(hash, HASH_DELETED)) { st_cleanup_safe(RHASH(hash)->ntbl, (st_data_t)Qundef); FL_UNSET(hash, HASH_DELETED); @@ -236,7 +243,8 @@ rb_hash_foreach(VALUE hash, int (*func)(ANYARGS), VALUE farg) arg.hash = hash; arg.func = (rb_foreach_func *)func; arg.arg = farg; - arg.iter_lev = RHASH_ITER_LEV(hash)++; + arg.marker = Data_Wrap_Struct(0, 0, 0, 0); + RHASH_ITER_LEV(hash)++; rb_ensure(hash_foreach_call, (VALUE)&arg, hash_foreach_ensure, (VALUE)&arg); } diff --git a/test/ruby/test_hash.rb b/test/ruby/test_hash.rb index e998c52621..4da8e234de 100644 --- a/test/ruby/test_hash.rb +++ b/test/ruby/test_hash.rb @@ -930,7 +930,9 @@ class TestHash < Test::Unit::TestCase h.clear c.call end + end + def test_callcc_iter_level bug9105 = '[ruby-dev:47803] [Bug #9105]' h = @cls[1=>2, 3=>4] c = nil @@ -948,6 +950,22 @@ class TestHash < Test::Unit::TestCase end end + def test_threaded_iter_level + bug9105 = '[ruby-dev:47807] [Bug #9105]' + h = @cls[1=>2] + 2.times.map { + f = false + th = Thread.start {h.each {f = true; sleep}} + Thread.pass until f + Thread.pass until th.stop? + th + }.each {|th| th.run; th.join} + assert_nothing_raised(RuntimeError, bug9105) do + h[5] = 6 + end + assert_equal(6, h[5], bug9105) + end + def test_compare_by_identity a = "foo" assert_not_predicate(@cls[], :compare_by_identity?) |