ruby-changes:31604
From: nobu <ko1@a...>
Date: Fri, 15 Nov 2013 22:21:44 +0900 (JST)
Subject: [ruby-changes:31604] nobu:r43683 (trunk): hash.c: iteration level with reentering
nobu 2013-11-15 22:21:38 +0900 (Fri, 15 Nov 2013) New Revision: 43683 http://svn.ruby-lang.org/cgi-bin/viewvc.cgi?view=rev&revision=43683 Log: 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] Modified files: trunk/ChangeLog trunk/hash.c trunk/test/ruby/test_hash.rb Index: ChangeLog =================================================================== --- ChangeLog (revision 43682) +++ ChangeLog (revision 43683) @@ -1,3 +1,10 @@ https://github.com/ruby/ruby/blob/trunk/ChangeLog#L1 +Fri Nov 15 22:21:34 2013 Nobuyoshi Nakada <nobu@r...> + + * 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@r...> * lib/delegate.rb (Delegator#send): override to get rid of global function interference. Index: hash.c =================================================================== --- hash.c (revision 43682) +++ hash.c (revision 43683) @@ -173,7 +173,7 @@ struct hash_foreach_arg { https://github.com/ruby/ruby/blob/trunk/hash.c#L173 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 https://github.com/ruby/ruby/blob/trunk/hash.c#L189 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) https://github.com/ruby/ruby/blob/trunk/hash.c#L211 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)( https://github.com/ruby/ruby/blob/trunk/hash.c#L243 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); } Index: test/ruby/test_hash.rb =================================================================== --- test/ruby/test_hash.rb (revision 43682) +++ test/ruby/test_hash.rb (revision 43683) @@ -930,7 +930,9 @@ class TestHash < Test::Unit::TestCase https://github.com/ruby/ruby/blob/trunk/test/ruby/test_hash.rb#L930 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 https://github.com/ruby/ruby/blob/trunk/test/ruby/test_hash.rb#L950 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?) -- ML: ruby-changes@q... Info: http://www.atdot.net/~ko1/quickml/