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

ruby-changes:1639

From: ko1@a...
Date: 22 Aug 2007 03:51:49 +0900
Subject: [ruby-changes:1639] ko1 - Ruby:r13130 (trunk): * cont.c: add Fiber#resume and Fiber.yield.

ko1	2007-08-22 03:51:39 +0900 (Wed, 22 Aug 2007)

  New Revision: 13130

  Modified files:
    trunk/ChangeLog
    trunk/cont.c
    trunk/enumerator.c
    trunk/include/ruby/intern.h
    trunk/test/ruby/test_fiber.rb
    trunk/version.h

  Log:
    * cont.c: add Fiber#resume and Fiber.yield.
      and Fiber::Core class to realize Coroutine.
    * include/ruby/intern.h: declare rb_fiber_yield(), rb_fiber_resume(),
    * enumerator.c: use above api.
    * test/ruby/test_fiber.rb: fix and add tests for above changes.
    


  http://svn.ruby-lang.org/cgi-bin/viewvc.cgi/trunk/version.h?r1=13130&r2=13129
  http://svn.ruby-lang.org/cgi-bin/viewvc.cgi/trunk/test/ruby/test_fiber.rb?r1=13130&r2=13129
  http://svn.ruby-lang.org/cgi-bin/viewvc.cgi/trunk/cont.c?r1=13130&r2=13129
  http://svn.ruby-lang.org/cgi-bin/viewvc.cgi/trunk/ChangeLog?r1=13130&r2=13129
  http://svn.ruby-lang.org/cgi-bin/viewvc.cgi/trunk/include/ruby/intern.h?r1=13130&r2=13129
  http://svn.ruby-lang.org/cgi-bin/viewvc.cgi/trunk/enumerator.c?r1=13130&r2=13129

Index: include/ruby/intern.h
===================================================================
--- include/ruby/intern.h	(revision 13129)
+++ include/ruby/intern.h	(revision 13130)
@@ -148,7 +148,8 @@
 int rb_cmpint(VALUE, VALUE, VALUE);
 NORETURN(void rb_cmperr(VALUE, VALUE));
 /* cont.c */
-VALUE rb_fiber_yield(VALUE fib, int argc, VALUE *args);
+VALUE rb_fiber_resume(VALUE fib, int argc, VALUE *args);
+VALUE rb_fiber_yield(int argc, VALUE *args);
 VALUE rb_fiber_current(void);
 /* enum.c */
 /* enumerator.c */
Index: ChangeLog
===================================================================
--- ChangeLog	(revision 13129)
+++ ChangeLog	(revision 13130)
@@ -1,3 +1,14 @@
+Wed Aug 22 03:51:07 2007  Koichi Sasada  <ko1@a...>
+
+	* cont.c: add Fiber#resume and Fiber.yield.
+	  and Fiber::Core class to realize Coroutine.
+
+	* include/ruby/intern.h: declare rb_fiber_yield(), rb_fiber_resume(),
+
+	* enumerator.c: use above api.
+
+	* test/ruby/test_fiber.rb: fix and add tests for above changes.
+
 Tue Aug 21 21:09:48 2007  Tanaka Akira  <akr@f...>
 
 	* lib/tmpdir.rb (Dir.mktmpdir): make directory suffix specifiable.
Index: enumerator.c
===================================================================
--- enumerator.c	(revision 13129)
+++ enumerator.c	(revision 13130)
@@ -373,7 +373,7 @@
     VALUE tmp = e->next;
 
     e->next = i;
-    tmp = rb_fiber_yield(e->dst, 1, &tmp);
+    tmp = rb_fiber_yield(1, &tmp);
     if (tmp != Qnil) {
 	e->dst = tmp;
     }
@@ -388,7 +388,7 @@
 
     rb_block_call(obj, rb_intern("each"), 0, 0, next_ii, obj);
     e->has_next = Qfalse;
-    rb_fiber_yield(e->dst, 1, &e->next);
+    rb_fiber_yield(1, &e->next);
 }
 
 static void
@@ -398,7 +398,7 @@
     e->dst = curr;
     e->fib = rb_block_call(rb_cFiber, rb_intern("new"), 0, 0, next_i, obj);
     e->has_next = Qtrue;
-    rb_fiber_yield(e->fib, 1, &curr);
+    rb_fiber_resume(e->fib, 1, &curr);
 }
 
 /*
@@ -432,7 +432,7 @@
 	rb_raise(rb_eStopIteration, "Enumerator#each reached at end");
     }
 
-    v = rb_fiber_yield(e->fib, 1, &curr);
+    v = rb_fiber_resume(e->fib, 1, &curr);
     return v;
 }
 
Index: cont.c
===================================================================
--- cont.c	(revision 13129)
+++ cont.c	(revision 13130)
@@ -18,7 +18,6 @@
 typedef struct rb_context_struct {
     VALUE self;
     VALUE value;
-    VALUE prev; /* for fiber */
     VALUE *vm_stack;
     VALUE *machine_stack;
     VALUE *machine_stack_src;
@@ -30,11 +29,14 @@
     rb_thread_t saved_thread;
     rb_jmpbuf_t jmpbuf;
     int machine_stack_size;
+    /* for cont */
+    VALUE prev;
     int alive;
 } rb_context_t;
 
 VALUE rb_cCont;
 VALUE rb_cFiber;
+VALUE rb_cFiberCore;
 VALUE rb_eFiberError;
 
 #define GetContPtr(obj, ptr)  \
@@ -52,7 +54,6 @@
 	rb_context_t *cont = ptr;
 	rb_gc_mark(cont->value);
 	rb_gc_mark(cont->prev);
-
 	rb_thread_mark(&cont->saved_thread);
 
 	if (cont->vm_stack) {
@@ -511,22 +512,40 @@
     return contval;
 }
 
-static void
-rb_fiber_terminate(rb_context_t *cont)
+static VALUE
+return_fiber(void)
 {
-    rb_context_t *prev_cont;
-    VALUE value = cont->value;
+    rb_context_t *cont;
+    VALUE curr = rb_fiber_current();
+    GetContPtr(curr, cont);
 
-    GetContPtr(cont->prev, prev_cont);
-    cont->alive = Qfalse;
-    if (prev_cont->alive == Qfalse) {
-	rb_fiber_yield(GET_THREAD()->root_fiber, 1, &value);
+    if (cont->prev == Qnil) {
+	rb_thread_t *th = GET_THREAD();
+
+	if (th->root_fiber != curr) {
+	    return th->root_fiber;
+	}
+	else {
+	    rb_raise(rb_eFiberError, "can't yield from root fiber");
+	}
     }
     else {
-	rb_fiber_yield(cont->prev, 1, &value);
+	VALUE prev = cont->prev;
+	cont->prev = Qnil;
+	return prev;
     }
 }
 
+VALUE rb_fiber_transfer(VALUE fib, int argc, VALUE *argv);
+
+static void
+rb_fiber_terminate(rb_context_t *cont)
+{
+    VALUE value = cont->value;
+    cont->alive = Qfalse;
+    rb_fiber_transfer(return_fiber(), 1, &value);
+}
+
 void
 rb_fiber_start(void)
 {
@@ -551,7 +570,13 @@
     TH_POP_TAG();
 
     if (state) {
-	th->thrown_errinfo = vm_make_jump_tag_but_local_jump(state, th->errinfo);
+	if (TAG_RAISE) {
+	    th->thrown_errinfo = th->errinfo;
+	}
+	else {
+	    th->thrown_errinfo =
+	      vm_make_jump_tag_but_local_jump(state, th->errinfo);
+	}
 	th->interrupt_flag = 1;
     }
 
@@ -565,7 +590,9 @@
     rb_thread_t *th = GET_THREAD();
     if (th->fiber == 0) {
 	/* save root */
-	th->root_fiber = th->fiber = cont_new(rb_cFiber)->self;
+	rb_context_t *cont = cont_new(rb_cFiberCore);
+	cont->prev = Qnil;
+	th->root_fiber = th->fiber = cont->self;
     }
     return th->fiber;
 }
@@ -583,12 +610,10 @@
     else {
 	/* create current fiber */
 	cont = cont_new(rb_cFiber); /* no need to allocate vm stack */
+	cont->prev = Qnil;
 	th->root_fiber = th->fiber = cont->self;
     }
 
-    if (cont->alive) {
-	next_cont->prev = cont->self;
-    }
     cont_save_machine_stack(th, cont);
 
     if (ruby_setjmp(cont->jmpbuf)) {
@@ -601,8 +626,8 @@
     }
 }
 
-VALUE
-rb_fiber_yield(VALUE fib, int argc, VALUE *argv)
+static inline VALUE
+fiber_switch(VALUE fib, int argc, VALUE *argv, int is_resume)
 {
     VALUE value;
     rb_context_t *cont;
@@ -613,38 +638,56 @@
     if (cont->saved_thread.self != th->self) {
 	rb_raise(rb_eFiberError, "fiber called across threads");
     }
-    if (cont->saved_thread.trap_tag != th->trap_tag) {
+    else if (cont->saved_thread.trap_tag != th->trap_tag) {
 	rb_raise(rb_eFiberError, "fiber called across trap");
     }
-    if (!cont->alive) {
+    else if (!cont->alive) {
 	rb_raise(rb_eFiberError, "dead fiber called");
     }
 
+    if (is_resume) {
+	cont->prev = rb_fiber_current();
+    }
     cont->value = make_passing_arg(argc, argv);
 
     if ((value = cont_store(cont)) == Qundef) {
 	cont_restore_0(cont, (VALUE *)&cont);
-	rb_bug("rb_fiber_yield: unreachable");
+	rb_bug("rb_fiber_resume: unreachable");
     }
-    
+
+    RUBY_VM_CHECK_INTS();
+
     return value;
 }
 
-static VALUE
-rb_fiber_m_yield(int argc, VALUE *argv, VALUE fib)
+VALUE
+rb_fiber_transfer(VALUE fib, int argc, VALUE *argv)
 {
-    return rb_fiber_yield(fib, argc, argv);
+    return fiber_switch(fib, argc, argv, 0);
 }
 
-static VALUE
-rb_fiber_prev(VALUE fib)
+VALUE
+rb_fiber_resume(VALUE fib, int argc, VALUE *argv)
 {
+    int i;
     rb_context_t *cont;
+    VALUE curr = rb_fiber_current();
     GetContPtr(fib, cont);
-    return cont->prev;
+
+    if (cont->prev != Qnil) {
+	rb_raise(rb_eFiberError, "double resume");
+    }
+
+    return fiber_switch(fib, argc, argv, 1);
 }
 
 VALUE
+rb_fiber_yield(int argc, VALUE *argv)
+{
+    rb_fiber_transfer(return_fiber(), argc, argv);
+}
+
+VALUE
 rb_fiber_alive_p(VALUE fib)
 {
     rb_context_t *cont;
@@ -653,23 +696,29 @@
 }
 
 static VALUE
-rb_fiber_s_current(VALUE klass)
+rb_fiber_m_resume(int argc, VALUE *argv, VALUE fib)
 {
-    return rb_fiber_current();
+    return rb_fiber_resume(fib, argc, argv);
 }
 
 static VALUE
-rb_fiber_s_prev(VALUE klass)
+rb_fiber_m_transfer(int argc, VALUE *argv, VALUE fib)
 {
-    return rb_fiber_prev(rb_fiber_s_current(Qnil));
+    return rb_fiber_transfer(fib, argc, argv);
 }
 
 static VALUE
-rb_fiber_s_yield(int argc, VALUE *argv, VALUE fib)
+rb_fiber_s_yield(int argc, VALUE *argv, VALUE klass)
 {
-    return rb_fiber_yield(rb_fiber_s_prev(Qnil), argc, argv);
+    return rb_fiber_yield(argc, argv);
 }
 
+static VALUE
+rb_fiber_s_current(VALUE klass)
+{
+    return rb_fiber_current();
+}
+
 void
 Init_Cont(void)
 {
@@ -682,15 +731,21 @@
 
     rb_cFiber = rb_define_class("Fiber", rb_cObject);
     rb_undef_alloc_func(rb_cFiber);
-    rb_define_method(rb_cFiber, "yield", rb_fiber_m_yield, -1);
-    rb_define_method(rb_cFiber, "prev", rb_fiber_prev, 0);
+    rb_define_method(rb_cFiber, "resume", rb_fiber_m_resume, -1);
     rb_define_method(rb_cFiber, "alive?", rb_fiber_alive_p, 0);
 
     rb_define_singleton_method(rb_cFiber, "current", rb_fiber_s_current, 0);
-    rb_define_singleton_method(rb_cFiber, "prev", rb_fiber_s_prev, 0);
     rb_define_singleton_method(rb_cFiber, "yield", rb_fiber_s_yield, -1);
     rb_define_singleton_method(rb_cFiber, "new", rb_fiber_s_new, 0);
 
+    rb_cFiberCore = rb_define_class_under(rb_cFiber, "Core", rb_cObject);
+    rb_undef_alloc_func(rb_cFiberCore);
+    rb_define_method(rb_cFiberCore, "transfer", rb_fiber_m_transfer, -1);
+    rb_define_method(rb_cFiberCore, "alive?", rb_fiber_alive_p, 0);
+
+    rb_define_singleton_method(rb_cFiberCore, "current", rb_fiber_s_current, 0);
+    rb_define_singleton_method(rb_cFiberCore, "new", rb_fiber_s_new, 0);
+    
     rb_eFiberError = rb_define_class("FiberError", rb_eStandardError);
 }
 
Index: version.h
===================================================================
--- version.h	(revision 13129)
+++ version.h	(revision 13130)
@@ -1,7 +1,7 @@
 #define RUBY_VERSION "1.9.0"
-#define RUBY_RELEASE_DATE "2007-08-21"
+#define RUBY_RELEASE_DATE "2007-08-22"
 #define RUBY_VERSION_CODE 190
-#define RUBY_RELEASE_CODE 20070821
+#define RUBY_RELEASE_CODE 20070822
 #define RUBY_PATCHLEVEL 0
 
 #define RUBY_VERSION_MAJOR 1
@@ -9,7 +9,7 @@
 #define RUBY_VERSION_TEENY 0
 #define RUBY_RELEASE_YEAR 2007
 #define RUBY_RELEASE_MONTH 8
-#define RUBY_RELEASE_DAY 21
+#define RUBY_RELEASE_DAY 22
 
 #ifdef RUBY_EXTERN
 RUBY_EXTERN const char ruby_version[];
Index: test/ruby/test_fiber.rb
===================================================================
--- test/ruby/test_fiber.rb	(revision 13129)
+++ test/ruby/test_fiber.rb	(revision 13130)
@@ -6,25 +6,24 @@
     assert_equal(:ok2,
       Fiber.new{|e|
         assert_equal(:ok1, e)
-        assert_equal(f, Fiber.prev)
         Fiber.yield :ok2
-      }.yield(:ok1)
+      }.resume(:ok1)
     )
-    assert_equal([:a, :b], Fiber.new{|a, b| [a, b]}.yield(:a, :b))
+    assert_equal([:a, :b], Fiber.new{|a, b| [a, b]}.resume(:a, :b))
   end
 
   def test_term
-    assert_equal(:ok, Fiber.new{:ok}.yield)
+    assert_equal(:ok, Fiber.new{:ok}.resume)
     assert_equal([:a, :b, :c, :d, :e],
       Fiber.new{
         Fiber.new{
           Fiber.new{
             Fiber.new{
               [:a]
-            }.yield + [:b]
-          }.yield + [:c]
-        }.yield + [:d]
-      }.yield + [:e])
+            }.resume + [:b]
+          }.resume + [:c]
+        }.resume + [:d]
+      }.resume + [:e])
   end
 
   def test_many_fibers
@@ -35,7 +34,7 @@
     assert_equal(max,
       max.times{|i|
         Fiber.new{
-        }.yield
+        }.resume
       }
     )
   end
@@ -48,7 +47,7 @@
         max.times{|i|
           Fiber.new{
             @cnt += 1
-          }.yield
+          }.resume
         }
       }
     }.each{|t|
@@ -63,50 +62,72 @@
     }
     assert_raise(FiberError){
       f = Fiber.new{}
-      Thread.new{f.yield}.join # Fiber yielding across thread
+      Thread.new{f.resume}.join # Fiber yielding across thread
     }
     assert_raise(FiberError){
       f = Fiber.new{}
-      f.yield
-      f.yield
+      f.resume
+      f.resume
     }
     assert_raise(RuntimeError){
       f = Fiber.new{
         @c = callcc{|c| @c = c}
-      }.yield
+      }.resume
       @c.call # cross fiber callcc
     }
-  end
-
-  def test_loop
-    ary = []
-    f2 = nil
-    f1 = Fiber.new{
-      ary << f2.yield(:foo)
-      :bar
+    assert_raise(RuntimeError){
+      Fiber.new{
+        raise
+      }.resume
     }
-    f2 = Fiber.new{
-      ary << f1.yield(:baz)
-      :ok
+    assert_raise(FiberError){
+      Fiber.yield
     }
-    assert_equal(:ok, f1.yield)
-    assert_equal([:baz, :bar], ary)
+    assert_raise(FiberError){
+      fib = Fiber.new{
+        fib.resume
+      }
+      fib.resume
+    }
+    assert_raise(FiberError){
+      fib = Fiber.new{
+        Fiber.new{
+          fib.resume
+        }.resume
+      }
+      fib.resume
+    }
   end
 
   def test_return
     assert_raise(LocalJumpError){
       Fiber.new do
         return
-      end.yield
+      end.resume
     }
   end
 
   def test_throw
-    assert_raise(RuntimeError){
+    assert_raise(NameError){
       Fiber.new do
         throw :a
-      end.yield
+      end.resume
     }
   end
+
+  def test_transfer
+    ary = []
+    f2 = nil
+    f1 = Fiber::Core.new{
+      ary << f2.transfer(:foo)
+      :ok
+    }
+    f2 = Fiber::Core.new{
+      ary << f1.transfer(:baz)
+      :ng
+    }
+    assert_equal(:ok, f1.transfer)
+    assert_equal([:baz], ary)
+  end
 end
 

--
ML: ruby-changes@q...
Info: http://www.atdot.net/~ko1/quickml

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