diff options
author | ko1 <ko1@b2dd03c8-39d4-4d8f-98ff-823fe69b080e> | 2007-08-21 18:51:39 +0000 |
---|---|---|
committer | ko1 <ko1@b2dd03c8-39d4-4d8f-98ff-823fe69b080e> | 2007-08-21 18:51:39 +0000 |
commit | c5e4cd063838033dff617f360a5b670b9eedaece (patch) | |
tree | 778e478e788a5284ebdb9798c7193233be428ac3 /cont.c | |
parent | c4d6f4c01e44aa48070d091649795c586f3583e6 (diff) | |
download | bundler-c5e4cd063838033dff617f360a5b670b9eedaece.tar.gz |
* 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.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@13130 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
Diffstat (limited to 'cont.c')
-rw-r--r-- | cont.c | 129 |
1 files changed, 92 insertions, 37 deletions
@@ -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 @@ typedef struct rb_context_struct { 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 @@ cont_mark(void *ptr) 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 @@ rb_fiber_s_new(VALUE self) 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 @@ rb_fiber_start(void) 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_fiber_current() 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 @@ cont_store(rb_context_t *next_cont) 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 @@ cont_store(rb_context_t *next_cont) } } -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,35 +638,53 @@ rb_fiber_yield(VALUE fib, int argc, VALUE *argv) 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 @@ -653,21 +696,27 @@ rb_fiber_alive_p(VALUE fib) } 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_m_transfer(int argc, VALUE *argv, VALUE fib) +{ + return rb_fiber_transfer(fib, argc, argv); } static VALUE -rb_fiber_s_prev(VALUE klass) +rb_fiber_s_yield(int argc, VALUE *argv, VALUE klass) { - return rb_fiber_prev(rb_fiber_s_current(Qnil)); + return rb_fiber_yield(argc, argv); } static VALUE -rb_fiber_s_yield(int argc, VALUE *argv, VALUE fib) +rb_fiber_s_current(VALUE klass) { - return rb_fiber_yield(rb_fiber_s_prev(Qnil), argc, argv); + return rb_fiber_current(); } void @@ -682,15 +731,21 @@ Init_Cont(void) 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); } |