From 57b0b6c7b13d44bd7655eee7e315d09d72ada640 Mon Sep 17 00:00:00 2001 From: matz Date: Mon, 6 Aug 2007 16:41:17 +0000 Subject: [PATCH] * cont.c (rb_fiber_yield): change argument ordering. export. * cont.c (rb_fiber_current): export * include/ruby/intern.h: export several functions from cont.c. * enumerator.c (enumerator_next): new method to implement external iterator (generator) using fiber. * enumerator.c (enumerator_next_p): new method to check whether any element is left in the generator sequence. * enumerator.c (enumerator_rewind): a new method to rewind the generator sequence. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@12891 b2dd03c8-39d4-4d8f-98ff-823fe69b080e --- ChangeLog | 17 +++++++ cont.c | 42 ++++++++--------- enumerator.c | 106 +++++++++++++++++++++++++++++++++++++++++- include/ruby/intern.h | 3 ++ include/ruby/ruby.h | 1 + 5 files changed, 145 insertions(+), 24 deletions(-) diff --git a/ChangeLog b/ChangeLog index e0e55cf095..53dbdcef6d 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,20 @@ +Tue Aug 7 01:27:47 2007 Yukihiro Matsumoto + + * cont.c (rb_fiber_yield): change argument ordering. export. + + * cont.c (rb_fiber_current): export + + * include/ruby/intern.h: export several functions from cont.c. + + * enumerator.c (enumerator_next): new method to implement external + iterator (generator) using fiber. + + * enumerator.c (enumerator_next_p): new method to check whether + any element is left in the generator sequence. + + * enumerator.c (enumerator_rewind): a new method to rewind the + generator sequence. + Tue Aug 7 01:15:24 2007 Yukihiro Matsumoto * enum.c (enum_cycle): new method to cycle enumerable forever. diff --git a/cont.c b/cont.c index 8d0d201540..bc5e9975de 100644 --- a/cont.c +++ b/cont.c @@ -212,14 +212,14 @@ cont_restore_1(rb_context_t *cont) } else { /* continuation */ - VALUE fval; + VALUE fib; th->fiber = sth->fiber; - fval = th->fiber ? th->fiber : th->root_fiber; + fib = th->fiber ? th->fiber : th->root_fiber; - if (fval) { + if (fib) { rb_context_t *fcont; - GetContPtr(fval, fcont); + GetContPtr(fib, fcont); th->stack_size = fcont->saved_thread.stack_size; th->stack = fcont->saved_thread.stack; } @@ -511,8 +511,6 @@ rb_fiber_s_new(VALUE self) return contval; } -static VALUE rb_fiber_yield(int argc, VALUE *args, VALUE fval); - static void rb_fiber_terminate(rb_context_t *cont) { @@ -521,12 +519,11 @@ rb_fiber_terminate(rb_context_t *cont) GetContPtr(cont->prev, prev_cont); cont->alive = Qfalse; - if (prev_cont->alive == Qfalse) { - rb_fiber_yield(1, &value, GET_THREAD()->root_fiber); + rb_fiber_yield(GET_THREAD()->root_fiber, 1, &value); } else { - rb_fiber_yield(1, &value, cont->prev); + rb_fiber_yield(cont->prev, 1, &value); } } @@ -562,9 +559,10 @@ rb_fiber_start(void) rb_bug("rb_fiber_start: unreachable"); } -static VALUE -rb_fiber_current(rb_thread_t *th) +VALUE +rb_fiber_current() { + rb_thread_t *th = GET_THREAD(); if (th->fiber == 0) { /* save root */ th->root_fiber = th->fiber = cont_new(rb_cFiber)->self; @@ -603,14 +601,14 @@ cont_store(rb_context_t *next_cont) } } -static VALUE -rb_fiber_yield(int argc, VALUE *argv, VALUE fval) +VALUE +rb_fiber_yield(VALUE fib, int argc, VALUE *argv) { VALUE value; rb_context_t *cont; rb_thread_t *th = GET_THREAD(); - GetContPtr(fval, cont); + GetContPtr(fib, cont); if (cont->saved_thread.self != th->self) { rb_raise(rb_eFiberError, "fiber called across threads"); @@ -633,25 +631,25 @@ rb_fiber_yield(int argc, VALUE *argv, VALUE fval) } static VALUE -rb_fiber_prev(VALUE fval) +rb_fiber_prev(VALUE fib) { rb_context_t *cont; - GetContPtr(fval, cont); + GetContPtr(fib, cont); return cont->prev; } -static VALUE -rb_fiber_alive_p(VALUE fval) +VALUE +rb_fiber_alive_p(VALUE fib) { rb_context_t *cont; - GetContPtr(fval, cont); + GetContPtr(fib, cont); return cont->alive; } static VALUE rb_fiber_s_current(VALUE klass) { - return rb_fiber_current(GET_THREAD()); + return rb_fiber_current(); } static VALUE @@ -661,9 +659,9 @@ rb_fiber_s_prev(VALUE klass) } static VALUE -rb_fiber_s_yield(int argc, VALUE *argv, VALUE fval) +rb_fiber_s_yield(int argc, VALUE *argv, VALUE fib) { - return rb_fiber_yield(argc, argv, rb_fiber_s_prev(Qnil)); + return rb_fiber_yield(rb_fiber_s_prev(Qnil), argc, argv); } void diff --git a/enumerator.c b/enumerator.c index d3bb173476..f7f8c33dbc 100644 --- a/enumerator.c +++ b/enumerator.c @@ -37,6 +37,8 @@ struct enumerator { VALUE proc; VALUE args; VALUE (*iter)(VALUE, struct enumerator *); + VALUE fib; + VALUE next; }; static void @@ -46,6 +48,8 @@ enumerator_mark(void *p) rb_gc_mark(ptr->method); rb_gc_mark(ptr->proc); rb_gc_mark(ptr->args); + rb_gc_mark(ptr->fib); + rb_gc_mark(ptr->next); } static struct enumerator * @@ -227,6 +231,8 @@ enumerator_init(VALUE enum_obj, VALUE obj, VALUE meth, int argc, VALUE *argv) ptr->iter = (VALUE (*)(VALUE, struct enumerator *))rb_yield; } if (argc) ptr->args = rb_ary_new4(argc, argv); + ptr->fib = 0; + ptr->next = Qundef; return enum_obj; } @@ -272,6 +278,7 @@ enumerator_init_copy(VALUE obj, VALUE orig) ptr1->proc = ptr0->proc; ptr1->iter = ptr0->iter; ptr1->args = ptr0->args; + ptr1->fib = ptr0->fib; return obj; } @@ -347,9 +354,101 @@ enumerator_with_index(VALUE obj) */ static VALUE -enumerator_to_splat(VALUE range) +enumerator_to_splat(VALUE obj) { - return rb_convert_type(range, T_ARRAY, "Array", "to_a"); + return rb_convert_type(obj, T_ARRAY, "Array", "to_a"); +} + +static VALUE +next_ii(VALUE i, VALUE *args) +{ + struct enumerator *e = enumerator_ptr(args[0]); + VALUE tmp = e->next; + + e->next = i; + if (tmp != Qundef) { + e->next = i; + rb_fiber_yield(args[1], 1, &tmp); + } + return Qnil; +} + +static VALUE +next_i(VALUE dummy, VALUE *args) +{ + VALUE tmp[2]; /* store in local variable */ + + + tmp[0] = args[0]; /* enumerator */ + tmp[1] = args[1]; /* current fibder */ + rb_block_call(args[0], rb_intern("each"), 0, 0, next_ii, (VALUE)tmp); + return enumerator_ptr(tmp[0])->next; +} + +/* + * call-seq: + * e.next => object + * + * Returns the next object in the enumerator, and move the internal + * position forward. When the position reached at the end, internal + * position is rewinded then IndexError is raised. + * + * Note that enumeration sequence by next method does not affect other + * non-external enumeration methods, unless underlying iteration + * methods itself has side-effect, e.g. IO#each_line. + * + */ + +static VALUE +enumerator_next(VALUE obj) +{ + struct enumerator *e = enumerator_ptr(obj); + VALUE args[2]; + VALUE v; + + args[0] = obj; + args[1] = rb_fiber_current(); + + if (!e->fib) { + e->fib = rb_block_call(rb_cFiber, rb_intern("new"), 0, 0, next_i, (VALUE)args); + } + else if (!rb_fiber_alive_p(e->fib)) { + e->fib = 0; + e->next = Qundef; + rb_raise(rb_eIndexError, "Enumerator#each reached at end"); + } + v = rb_fiber_yield(e->fib, 0, 0); + return v; +} + +/* + * call-seq: + * e.next? => bool + * + * Returns true if this enumerator object has not reached the end yet. + */ + +static VALUE +enumerator_next_p(VALUE obj) +{ + return rb_fiber_alive_p(enumerator_ptr(obj)->fib); +} + +/* + * call-seq: + * e.next? => e + * + * Rewinds the enumeration sequence by the next method. + */ + +static VALUE +enumerator_rewind(VALUE obj) +{ + struct enumerator *e = enumerator_ptr(obj); + + e->fib = 0; + e->next = Qundef; + return obj; } void @@ -370,6 +469,9 @@ Init_Enumerator(void) rb_define_method(rb_cEnumerator, "each", enumerator_each, 0); rb_define_method(rb_cEnumerator, "with_index", enumerator_with_index, 0); rb_define_method(rb_cEnumerator, "to_splat", enumerator_to_splat, 0); + rb_define_method(rb_cEnumerator, "next", enumerator_next, 0); + rb_define_method(rb_cEnumerator, "next?", enumerator_next_p, 0); + rb_define_method(rb_cEnumerator, "rewind", enumerator_rewind, 0); sym_each = ID2SYM(rb_intern("each")); sym_each_with_index = ID2SYM(rb_intern("each_with_index")); diff --git a/include/ruby/intern.h b/include/ruby/intern.h index 7d5970762e..a57bb1ed9e 100644 --- a/include/ruby/intern.h +++ b/include/ruby/intern.h @@ -147,6 +147,9 @@ VALUE rb_singleton_class(VALUE); /* compar.c */ 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_current(void); /* enum.c */ /* enumerator.c */ VALUE rb_enumeratorize(VALUE, VALUE, int, VALUE *); diff --git a/include/ruby/ruby.h b/include/ruby/ruby.h index ecf9088c79..8931421672 100644 --- a/include/ruby/ruby.h +++ b/include/ruby/ruby.h @@ -760,6 +760,7 @@ RUBY_EXTERN VALUE rb_cCont; RUBY_EXTERN VALUE rb_cDir; RUBY_EXTERN VALUE rb_cData; RUBY_EXTERN VALUE rb_cFalseClass; +RUBY_EXTERN VALUE rb_cFiber; RUBY_EXTERN VALUE rb_cFile; RUBY_EXTERN VALUE rb_cFixnum; RUBY_EXTERN VALUE rb_cFloat;