[前][次][番号順一覧][スレッド一覧]

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/

[前][次][番号順一覧][スレッド一覧]