diff options
Diffstat (limited to 'trunk/cont.c')
-rw-r--r-- | trunk/cont.c | 780 |
1 files changed, 0 insertions, 780 deletions
diff --git a/trunk/cont.c b/trunk/cont.c deleted file mode 100644 index ad2c358264..0000000000 --- a/trunk/cont.c +++ /dev/null @@ -1,780 +0,0 @@ -/********************************************************************** - - cont.c - - - $Author$ - created at: Thu May 23 09:03:43 2007 - - Copyright (C) 2007 Koichi Sasada - -**********************************************************************/ - -#include "ruby/ruby.h" -#include "vm_core.h" -#include "gc.h" -#include "eval_intern.h" - -enum context_type { - CONTINUATION_CONTEXT = 0, - FIBER_CONTEXT = 1, - ROOT_FIBER_CONTEXT = 2 -}; - -typedef struct rb_context_struct { - VALUE self; - VALUE value; - VALUE *vm_stack; - VALUE *machine_stack; - VALUE *machine_stack_src; -#ifdef __ia64 - VALUE *machine_register_stack; - VALUE *machine_register_stack_src; - int machine_register_stack_size; -#endif - rb_thread_t saved_thread; - rb_jmpbuf_t jmpbuf; - int machine_stack_size; - VALUE prev; - int alive; - enum context_type type; -} rb_context_t; - -static VALUE rb_cContinuation; -static VALUE rb_cFiber; -static VALUE rb_eFiberError; - -#define GetContPtr(obj, ptr) \ - Data_Get_Struct(obj, rb_context_t, ptr) - -NOINLINE(static VALUE cont_capture(volatile int *stat)); - -void rb_thread_mark(rb_thread_t *th); - -static void -cont_mark(void *ptr) -{ - RUBY_MARK_ENTER("cont"); - if (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) { - rb_gc_mark_locations(cont->vm_stack, - cont->vm_stack + cont->saved_thread.stack_size); - } - - if (cont->machine_stack) { - rb_gc_mark_locations(cont->machine_stack, - cont->machine_stack + cont->machine_stack_size); - } -#ifdef __ia64 - if (cont->machine_register_stack) { - rb_gc_mark_locations(cont->machine_register_stack, - cont->machine_register_stack + cont->machine_register_stack_size); - } -#endif - } - RUBY_MARK_LEAVE("cont"); -} - -static void -cont_free(void *ptr) -{ - RUBY_FREE_ENTER("cont"); - if (ptr) { - rb_context_t *cont = ptr; - RUBY_FREE_UNLESS_NULL(cont->saved_thread.stack); - RUBY_FREE_UNLESS_NULL(cont->machine_stack); -#ifdef __ia64 - RUBY_FREE_UNLESS_NULL(cont->machine_register_stack); -#endif - RUBY_FREE_UNLESS_NULL(cont->vm_stack); - - if (cont->type == FIBER_CONTEXT) { - st_free_table(cont->saved_thread.local_storage); - } - - ruby_xfree(ptr); - } - RUBY_FREE_LEAVE("cont"); -} - -static void -cont_save_machine_stack(rb_thread_t *th, rb_context_t *cont) -{ - int size; - rb_thread_t *sth = &cont->saved_thread; - - SET_MACHINE_STACK_END(&th->machine_stack_end); -#ifdef __ia64 - th->machine_register_stack_end = rb_ia64_bsp(); -#endif - - if (th->machine_stack_start > th->machine_stack_end) { - size = cont->machine_stack_size = th->machine_stack_start - th->machine_stack_end; - cont->machine_stack_src = th->machine_stack_end; - } - else { - size = cont->machine_stack_size = th->machine_stack_end - th->machine_stack_start; - cont->machine_stack_src = th->machine_stack_start; - } - - if (cont->machine_stack) { - REALLOC_N(cont->machine_stack, VALUE, size); - } - else { - cont->machine_stack = ALLOC_N(VALUE, size); - } - - FLUSH_REGISTER_WINDOWS; - MEMCPY(cont->machine_stack, cont->machine_stack_src, VALUE, size); - -#ifdef __ia64 - rb_ia64_flushrs(); - size = cont->machine_register_stack_size = th->machine_register_stack_end - th->machine_register_stack_start; - cont->machine_register_stack_src = th->machine_register_stack_start; - if (cont->machine_register_stack) { - REALLOC_N(cont->machine_register_stack, VALUE, size); - } - else { - cont->machine_register_stack = ALLOC_N(VALUE, size); - } - - MEMCPY(cont->machine_register_stack, cont->machine_register_stack_src, VALUE, size); -#endif - - sth->machine_stack_start = sth->machine_stack_end = 0; -#ifdef __ia64 - sth->machine_register_stack_start = sth->machine_register_stack_end = 0; -#endif -} - -static rb_context_t * -cont_new(VALUE klass) -{ - rb_context_t *cont; - volatile VALUE contval; - rb_thread_t *th = GET_THREAD(); - - contval = Data_Make_Struct(klass, rb_context_t, cont_mark, cont_free, cont); - - cont->self = contval; - cont->alive = Qtrue; - - /* save thread context */ - cont->saved_thread = *th; - - return cont; -} - -void vm_stack_to_heap(rb_thread_t *th); - -static VALUE -cont_capture(volatile int *stat) -{ - rb_context_t *cont; - rb_thread_t *th = GET_THREAD(), *sth; - volatile VALUE contval; - - vm_stack_to_heap(th); - cont = cont_new(rb_cContinuation); - contval = cont->self; - sth = &cont->saved_thread; - - cont->vm_stack = ALLOC_N(VALUE, th->stack_size); - MEMCPY(cont->vm_stack, th->stack, VALUE, th->stack_size); - sth->stack = 0; - - cont_save_machine_stack(th, cont); - - if (ruby_setjmp(cont->jmpbuf)) { - VALUE value; - - value = cont->value; - cont->value = Qnil; - *stat = 1; - return value; - } - else { - *stat = 0; - return cont->self; - } -} - -NORETURN(static void cont_restore_1(rb_context_t *)); - -static void -cont_restore_1(rb_context_t *cont) -{ - rb_thread_t *th = GET_THREAD(), *sth = &cont->saved_thread; - - /* restore thread context */ - if (cont->type == CONTINUATION_CONTEXT) { - /* continuation */ - VALUE fib; - - th->fiber = sth->fiber; - fib = th->fiber ? th->fiber : th->root_fiber; - - if (fib) { - rb_context_t *fcont; - GetContPtr(fib, fcont); - th->stack_size = fcont->saved_thread.stack_size; - th->stack = fcont->saved_thread.stack; - } - MEMCPY(th->stack, cont->vm_stack, VALUE, sth->stack_size); - } - else { - /* fiber */ - th->stack = sth->stack; - th->stack_size = sth->stack_size; - th->local_storage = sth->local_storage; - th->fiber = cont->self; - } - - th->cfp = sth->cfp; - th->safe_level = sth->safe_level; - th->raised_flag = sth->raised_flag; - th->state = sth->state; - th->status = sth->status; - th->tag = sth->tag; - th->trap_tag = sth->trap_tag; - th->errinfo = sth->errinfo; - th->first_proc = sth->first_proc; - - /* restore machine stack */ -#ifdef _M_AMD64 - { - /* workaround for x64 SEH */ - jmp_buf buf; - setjmp(buf); - ((_JUMP_BUFFER*)(&cont->jmpbuf))->Frame = - ((_JUMP_BUFFER*)(&buf))->Frame; - } -#endif - if (cont->machine_stack_src) { - FLUSH_REGISTER_WINDOWS; - MEMCPY(cont->machine_stack_src, cont->machine_stack, - VALUE, cont->machine_stack_size); - } - -#ifdef __ia64 - if (cont->machine_register_stack_src) { - MEMCPY(cont->machine_register_stack_src, cont->machine_register_stack, - VALUE, cont->machine_register_stack_size); - } -#endif - - ruby_longjmp(cont->jmpbuf, 1); -} - -NORETURN(NOINLINE(static void cont_restore_0(rb_context_t *, VALUE *))); - -#ifdef __ia64 -#define C(a) rse_##a##0, rse_##a##1, rse_##a##2, rse_##a##3, rse_##a##4 -#define E(a) rse_##a##0= rse_##a##1= rse_##a##2= rse_##a##3= rse_##a##4 -static volatile int C(a), C(b), C(c), C(d), C(e); -static volatile int C(f), C(g), C(h), C(i), C(j); -static volatile int C(k), C(l), C(m), C(n), C(o); -static volatile int C(p), C(q), C(r), C(s), C(t); -int rb_dummy_false = 0; -NORETURN(NOINLINE(static void register_stack_extend(rb_context_t *, VALUE *))); -static void -register_stack_extend(rb_context_t *cont, VALUE *curr_bsp) -{ - if (rb_dummy_false) { - /* use registers as much as possible */ - E(a) = E(b) = E(c) = E(d) = E(e) = - E(f) = E(g) = E(h) = E(i) = E(j) = - E(k) = E(l) = E(m) = E(n) = E(o) = - E(p) = E(q) = E(r) = E(s) = E(t) = 0; - E(a) = E(b) = E(c) = E(d) = E(e) = - E(f) = E(g) = E(h) = E(i) = E(j) = - E(k) = E(l) = E(m) = E(n) = E(o) = - E(p) = E(q) = E(r) = E(s) = E(t) = 0; - } - if (curr_bsp < cont->machine_register_stack_src+cont->machine_register_stack_size) { - register_stack_extend(cont, (VALUE*)rb_ia64_bsp()); - } - cont_restore_1(cont); -} -#undef C -#undef E -#endif - -static void -cont_restore_0(rb_context_t *cont, VALUE *addr_in_prev_frame) -{ - if (cont->machine_stack_src) { -#define STACK_PAD_SIZE 1024 - VALUE space[STACK_PAD_SIZE]; - -#if STACK_GROW_DIRECTION < 0 /* downward */ - if (addr_in_prev_frame > cont->machine_stack_src) { - cont_restore_0(cont, &space[0]); - } -#elif STACK_GROW_DIRECTION > 0 /* upward */ - if (addr_in_prev_frame < cont->machine_stack_src + cont->machine_stack_size) { - cont_restore_0(cont, &space[STACK_PAD_SIZE-1]); - } -#else - if (addr_in_prev_frame > &space[0]) { - /* Stack grows downward */ - if (addr_in_prev_frame > cont->machine_stack_src) { - cont_restore_0(cont, &space[0]); - } - } - else { - /* Stack grows upward */ - if (addr_in_prev_frame < cont->machine_stack_src + cont->machine_stack_size) { - cont_restore_0(cont, &space[STACK_PAD_SIZE-1]); - } - } -#endif - } -#ifdef __ia64 - register_stack_extend(cont, (VALUE*)rb_ia64_bsp()); -#else - cont_restore_1(cont); -#endif -} - -/* - * Document-class: Continuation - * - * Continuation objects are generated by - * <code>Kernel#callcc</code>. They hold a return address and execution - * context, allowing a nonlocal return to the end of the - * <code>callcc</code> block from anywhere within a program. - * Continuations are somewhat analogous to a structured version of C's - * <code>setjmp/longjmp</code> (although they contain more state, so - * you might consider them closer to threads). - * - * For instance: - * - * arr = [ "Freddie", "Herbie", "Ron", "Max", "Ringo" ] - * callcc{|$cc|} - * puts(message = arr.shift) - * $cc.call unless message =~ /Max/ - * - * <em>produces:</em> - * - * Freddie - * Herbie - * Ron - * Max - * - * This (somewhat contrived) example allows the inner loop to abandon - * processing early: - * - * callcc {|cont| - * for i in 0..4 - * print "\n#{i}: " - * for j in i*5...(i+1)*5 - * cont.call() if j == 17 - * printf "%3d", j - * end - * end - * } - * print "\n" - * - * <em>produces:</em> - * - * 0: 0 1 2 3 4 - * 1: 5 6 7 8 9 - * 2: 10 11 12 13 14 - * 3: 15 16 - */ - -/* - * call-seq: - * callcc {|cont| block } => obj - * - * Generates a <code>Continuation</code> object, which it passes to the - * associated block. Performing a <em>cont</em><code>.call</code> will - * cause the <code>callcc</code> to return (as will falling through the - * end of the block). The value returned by the <code>callcc</code> is - * the value of the block, or the value passed to - * <em>cont</em><code>.call</code>. See class <code>Continuation</code> - * for more details. Also see <code>Kernel::throw</code> for - * an alternative mechanism for unwinding a call stack. - */ - -static VALUE -rb_callcc(VALUE self) -{ - volatile int called; - volatile VALUE val = cont_capture(&called); - - if (called) { - return val; - } - else { - return rb_yield(val); - } -} - -static VALUE -make_passing_arg(int argc, VALUE *argv) -{ - switch(argc) { - case 0: - return Qnil; - case 1: - return argv[0]; - default: - return rb_ary_new4(argc, argv); - } -} - -/* - * call-seq: - * cont.call(args, ...) - * cont[args, ...] - * - * Invokes the continuation. The program continues from the end of the - * <code>callcc</code> block. If no arguments are given, the original - * <code>callcc</code> returns <code>nil</code>. If one argument is - * given, <code>callcc</code> returns it. Otherwise, an array - * containing <i>args</i> is returned. - * - * callcc {|cont| cont.call } #=> nil - * callcc {|cont| cont.call 1 } #=> 1 - * callcc {|cont| cont.call 1, 2, 3 } #=> [1, 2, 3] - */ - -static VALUE -rb_cont_call(int argc, VALUE *argv, VALUE contval) -{ - rb_context_t *cont; - rb_thread_t *th = GET_THREAD(); - GetContPtr(contval, cont); - - if (cont->saved_thread.self != th->self) { - rb_raise(rb_eRuntimeError, "continuation called across threads"); - } - if (cont->saved_thread.trap_tag != th->trap_tag) { - rb_raise(rb_eRuntimeError, "continuation called across trap"); - } - if (cont->saved_thread.fiber) { - rb_context_t *fcont; - GetContPtr(cont->saved_thread.fiber, fcont); - - if (th->fiber != cont->saved_thread.fiber) { - rb_raise(rb_eRuntimeError, "continuation called across fiber"); - } - - if (!fcont->alive) { - rb_raise(rb_eRuntimeError, "continuation called dead fiber"); - } - } - - cont->value = make_passing_arg(argc, argv); - - cont_restore_0(cont, &contval); - return Qnil; /* unreachable */ -} - -/*********/ -/* fiber */ -/*********/ - -#define FIBER_VM_STACK_SIZE (4 * 1024) - -static rb_context_t * -fiber_alloc(VALUE klass) -{ - rb_context_t *cont = cont_new(klass); - - cont->type = FIBER_CONTEXT; - cont->prev = Qnil; - - return cont; -} - -static VALUE -fiber_new(VALUE klass, VALUE proc) -{ - rb_context_t *cont = fiber_alloc(klass); - VALUE contval = cont->self; - rb_thread_t *th = &cont->saved_thread; - - /* initialize */ - cont->vm_stack = 0; - - th->stack = 0; - th->stack_size = FIBER_VM_STACK_SIZE; - th->stack = ALLOC_N(VALUE, th->stack_size); - - th->cfp = (void *)(th->stack + th->stack_size); - th->cfp--; - th->cfp->pc = 0; - th->cfp->sp = th->stack + 1; - th->cfp->bp = 0; - th->cfp->lfp = th->stack; - *th->cfp->lfp = 0; - th->cfp->dfp = th->stack; - th->cfp->self = Qnil; - th->cfp->flag = 0; - th->cfp->iseq = 0; - th->cfp->proc = 0; - th->cfp->block_iseq = 0; - th->tag = 0; - th->local_storage = st_init_numtable(); - - th->first_proc = proc; - - MEMCPY(&cont->jmpbuf, &th->root_jmpbuf, rb_jmpbuf_t, 1); - - return contval; -} - -VALUE -rb_fiber_new(VALUE (*func)(ANYARGS), VALUE obj) -{ - return fiber_new(rb_cFiber, rb_proc_new(func, obj)); -} - -static VALUE -rb_fiber_s_new(VALUE self) -{ - return fiber_new(self, rb_block_proc()); -} - -static VALUE -return_fiber(void) -{ - rb_context_t *cont; - VALUE curr = rb_fiber_current(); - GetContPtr(curr, cont); - - 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 { - 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) -{ - rb_thread_t *th = GET_THREAD(); - rb_context_t *cont; - rb_proc_t *proc; - VALUE args; - int state; - - GetContPtr(th->fiber, cont); - TH_PUSH_TAG(th); - if ((state = EXEC_TAG()) == 0) { - GetProcPtr(cont->saved_thread.first_proc, proc); - args = cont->value; - cont->value = Qnil; - th->errinfo = Qnil; - th->local_lfp = proc->block.lfp; - th->local_svar = Qnil; - - cont->value = vm_invoke_proc(th, proc, proc->block.self, 1, &args, 0); - } - TH_POP_TAG(); - - if (state) { - if (TAG_RAISE) { - th->thrown_errinfo = th->errinfo; - } - else { - th->thrown_errinfo = - vm_make_jump_tag_but_local_jump(state, th->errinfo); - } - RUBY_VM_SET_INTERRUPT(th); - } - - rb_fiber_terminate(cont); - rb_bug("rb_fiber_start: unreachable"); -} - -VALUE -rb_fiber_current() -{ - rb_thread_t *th = GET_THREAD(); - if (th->fiber == 0) { - /* save root */ - rb_context_t *cont = fiber_alloc(rb_cFiber); - cont->type = ROOT_FIBER_CONTEXT; - th->root_fiber = th->fiber = cont->self; - } - return th->fiber; -} - -static VALUE -fiber_store(rb_context_t *next_cont) -{ - rb_thread_t *th = GET_THREAD(); - rb_context_t *cont; - - if (th->fiber) { - GetContPtr(th->fiber, cont); - cont->saved_thread = *th; - } - else { - /* create current fiber */ - cont = fiber_alloc(rb_cFiber); /* no need to allocate vm stack */ - cont->type = ROOT_FIBER_CONTEXT; - th->root_fiber = th->fiber = cont->self; - } - - cont_save_machine_stack(th, cont); - - if (ruby_setjmp(cont->jmpbuf)) { - /* restored */ - GetContPtr(th->fiber, cont); - return cont->value; - } - else { - return Qundef; - } -} - -static inline VALUE -fiber_switch(VALUE fib, int argc, VALUE *argv, int is_resume) -{ - VALUE value; - rb_context_t *cont; - rb_thread_t *th = GET_THREAD(); - - GetContPtr(fib, cont); - - if (cont->saved_thread.self != th->self) { - rb_raise(rb_eFiberError, "fiber called across threads"); - } - else if (cont->saved_thread.trap_tag != th->trap_tag) { - rb_raise(rb_eFiberError, "fiber called across trap"); - } - 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 = fiber_store(cont)) == Qundef) { - cont_restore_0(cont, &value); - rb_bug("rb_fiber_resume: unreachable"); - } - - RUBY_VM_CHECK_INTS(); - - return value; -} - -VALUE -rb_fiber_transfer(VALUE fib, int argc, VALUE *argv) -{ - return fiber_switch(fib, argc, argv, 0); -} - -VALUE -rb_fiber_resume(VALUE fib, int argc, VALUE *argv) -{ - rb_context_t *cont; - GetContPtr(fib, cont); - - 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) -{ - return rb_fiber_transfer(return_fiber(), argc, argv); -} - -VALUE -rb_fiber_alive_p(VALUE fib) -{ - rb_context_t *cont; - GetContPtr(fib, cont); - return cont->alive; -} - -static VALUE -rb_fiber_m_resume(int argc, VALUE *argv, VALUE fib) -{ - 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_yield(int argc, VALUE *argv, VALUE klass) -{ - return rb_fiber_yield(argc, argv); -} - -static VALUE -rb_fiber_s_current(VALUE klass) -{ - return rb_fiber_current(); -} - -void -Init_Cont(void) -{ - rb_cFiber = rb_define_class("Fiber", rb_cObject); - rb_undef_alloc_func(rb_cFiber); - rb_eFiberError = rb_define_class("FiberError", rb_eStandardError); - rb_define_singleton_method(rb_cFiber, "new", rb_fiber_s_new, 0); - rb_define_singleton_method(rb_cFiber, "yield", rb_fiber_s_yield, -1); - rb_define_method(rb_cFiber, "resume", rb_fiber_m_resume, -1); -} - -void -Init_Continuation_body(void) -{ - rb_cContinuation = rb_define_class("Continuation", rb_cObject); - rb_undef_alloc_func(rb_cContinuation); - rb_undef_method(CLASS_OF(rb_cContinuation), "new"); - rb_define_method(rb_cContinuation, "call", rb_cont_call, -1); - rb_define_method(rb_cContinuation, "[]", rb_cont_call, -1); - rb_define_global_function("callcc", rb_callcc, 0); -} - -void -Init_Fiber_as_Coroutine(void) -{ - rb_define_method(rb_cFiber, "transfer", rb_fiber_m_transfer, -1); - rb_define_method(rb_cFiber, "alive?", rb_fiber_alive_p, 0); - rb_define_singleton_method(rb_cFiber, "current", rb_fiber_s_current, 0); -} |