Created
August 5, 2012 01:18
-
-
Save wanabe/3260977 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| diff --git a/cont.c b/cont.c | |
| index 58b2d30..b73931b 100644 | |
| --- a/cont.c | |
| +++ b/cont.c | |
| @@ -282,6 +282,19 @@ fiber_mark(void *ptr) | |
| RUBY_MARK_LEAVE("cont"); | |
| } | |
| +void | |
| +rb_fibers_mark(VALUE fibval) { | |
| + rb_fiber_t *fib, *root_fib; | |
| + | |
| + if (!RTEST(fibval)) return; | |
| + GetFiberPtr(fibval, root_fib); | |
| + fib = root_fib->prev_fiber; | |
| + while (fib != root_fib) { | |
| + rb_gc_mark(fib->cont.self); | |
| + fib= fib->prev_fiber; | |
| + } | |
| +} | |
| + | |
| static void | |
| fiber_link_join(rb_fiber_t *fib) | |
| { | |
| @@ -880,6 +893,7 @@ make_passing_arg(int argc, VALUE *argv) | |
| { | |
| switch(argc) { | |
| case 0: | |
| + case -2: | |
| return Qnil; | |
| case 1: | |
| return argv[0]; | |
| @@ -1133,13 +1147,32 @@ rb_fiber_terminate(rb_fiber_t *fib) | |
| rb_fiber_transfer(return_fiber(), 1, &value); | |
| } | |
| +static VALUE | |
| +fiber_start_i(VALUE tag, rb_context_t *cont) | |
| +{ | |
| + rb_thread_t *th = GET_THREAD(); | |
| + rb_proc_t *proc; | |
| + int argc; | |
| + VALUE *argv, args; | |
| + GetProcPtr(cont->saved_thread.first_proc, proc); | |
| + args = cont->value; | |
| + argv = (argc = cont->argc) > 1 ? RARRAY_PTR(args) : &args; | |
| + cont->value = Qnil; | |
| + th->errinfo = Qnil; | |
| + th->root_lep = rb_vm_ep_local_ep(proc->block.ep); | |
| + th->root_svar = Qnil; | |
| + | |
| + cont->value = rb_vm_invoke_proc(th, proc, proc->block.self, argc, argv, 0); | |
| + | |
| + return Qfalse; | |
| +} | |
| + | |
| void | |
| rb_fiber_start(void) | |
| { | |
| rb_thread_t *th = GET_THREAD(); | |
| rb_fiber_t *fib; | |
| rb_context_t *cont; | |
| - rb_proc_t *proc; | |
| int state; | |
| GetFiberPtr(th->fiber, fib); | |
| @@ -1147,18 +1180,8 @@ rb_fiber_start(void) | |
| TH_PUSH_TAG(th); | |
| if ((state = EXEC_TAG()) == 0) { | |
| - int argc; | |
| - VALUE *argv, args; | |
| - GetProcPtr(cont->saved_thread.first_proc, proc); | |
| - args = cont->value; | |
| - argv = (argc = cont->argc) > 1 ? RARRAY_PTR(args) : &args; | |
| - cont->value = Qnil; | |
| - th->errinfo = Qnil; | |
| - th->root_lep = rb_vm_ep_local_ep(proc->block.ep); | |
| - th->root_svar = Qnil; | |
| - | |
| fib->status = RUNNING; | |
| - cont->value = rb_vm_invoke_proc(th, proc, proc->block.self, argc, argv, 0); | |
| + rb_catch_obj(-2, fiber_start_i, (VALUE)cont); | |
| } | |
| TH_POP_TAG(); | |
| @@ -1253,6 +1276,7 @@ fiber_store(rb_fiber_t *next_fib) | |
| /* restored */ | |
| GetFiberPtr(th->fiber, fib); | |
| if (fib->cont.argc == -1) rb_exc_raise(fib->cont.value); | |
| + else if (fib->cont.argc == -2) rb_throw_obj(-2, Qnil); | |
| return fib->cont.value; | |
| } | |
| #if !FIBER_USE_NATIVE | |
| @@ -1496,6 +1520,56 @@ rb_fiber_s_current(VALUE klass) | |
| return rb_fiber_current(); | |
| } | |
| +static void | |
| +fiber_terminate_on_th(rb_thread_t *th, int ignore_mark) | |
| +{ | |
| + VALUE fibval, current_fibval; | |
| + rb_fiber_t *fib, *root_fib, *prev_fiber; | |
| + rb_thread_t *_th = GET_THREAD(); | |
| + | |
| + fibval = th->root_fiber; | |
| + if (!RTEST(fibval)) return; | |
| + GetFiberPtr(fibval, root_fib); | |
| + | |
| + rb_thread_set_current(th); | |
| + fib = root_fib->prev_fiber; | |
| + current_fibval = rb_fiber_current(); | |
| + while (fib != root_fib) { | |
| + prev_fiber = fib->prev_fiber; | |
| + fibval = fib->cont.self; | |
| + if (fib->status == RUNNING && current_fibval != fibval) { | |
| + if(ignore_mark || !rb_gc_marked_p(fibval)) { | |
| + fib->prev = current_fibval; | |
| + fiber_switch(fibval, -2, 0, 0); | |
| + } | |
| + } | |
| + fib = prev_fiber; | |
| + } | |
| + rb_thread_set_current(_th); | |
| +} | |
| + | |
| +static int | |
| +fiber_terminate_unused_i(st_data_t key, st_data_t val, st_data_t data) | |
| +{ | |
| + rb_thread_t *th; | |
| + | |
| + GetThreadPtr((VALUE)key, th); | |
| + fiber_terminate_on_th(th, FALSE); | |
| + return ST_CONTINUE; | |
| +} | |
| + | |
| +void | |
| +rb_fiber_terminate_unused(void) | |
| +{ | |
| + st_foreach(GET_VM()->living_threads, fiber_terminate_unused_i, 0); | |
| +} | |
| + | |
| +void | |
| +rb_thread_terminate_fibers(rb_thread_t *th) | |
| +{ | |
| + fiber_terminate_on_th(th, TRUE); | |
| +} | |
| + | |
| /* | |
| diff --git a/eval.c b/eval.c | |
| index 8f3196d..54dafbe 100644 | |
| --- a/eval.c | |
| +++ b/eval.c | |
| @@ -158,6 +158,12 @@ ruby_cleanup(volatile int ex) | |
| rb_thread_t *th = GET_THREAD(); | |
| int nerr; | |
| + PUSH_TAG(); | |
| + if ((state = EXEC_TAG()) == 0) { | |
| + rb_thread_terminate_fibers(th); | |
| + } | |
| + POP_TAG(); | |
| + | |
| rb_threadptr_interrupt(th); | |
| rb_threadptr_check_signal(th); | |
| PUSH_TAG(); | |
| diff --git a/gc.c b/gc.c | |
| index c6e1edc..7ff6f3b 100644 | |
| --- a/gc.c | |
| +++ b/gc.c | |
| @@ -2311,6 +2311,12 @@ ready_to_gc(rb_objspace_t *objspace) | |
| return TRUE; | |
| } | |
| +int | |
| +rb_gc_marked_p(VALUE p) | |
| +{ | |
| + return MARKED_IN_BITMAP(GET_HEAP_BITMAP(p), p); | |
| +} | |
| + | |
| static void | |
| before_gc_sweep(rb_objspace_t *objspace) | |
| { | |
| diff --git a/internal.h b/internal.h | |
| index 08291af..96bf151 100644 | |
| --- a/internal.h | |
| +++ b/internal.h | |
| @@ -70,6 +70,8 @@ VALUE rb_insns_name_array(void); | |
| /* cont.c */ | |
| VALUE rb_obj_is_fiber(VALUE); | |
| void rb_fiber_reset_root_local_storage(VALUE); | |
| +void rb_fiber_terminate_unused(void); | |
| +void rb_fibers_mark(VALUE); | |
| /* debug.c */ | |
| PRINTF_ARGS(void ruby_debug_printf(const char*, ...), 1, 2); | |
| @@ -107,6 +109,7 @@ void Init_File(void); | |
| /* gc.c */ | |
| void Init_heap(void); | |
| void *ruby_mimmalloc(size_t size); | |
| +int rb_gc_marked_p(VALUE); | |
| /* inits.c */ | |
| void rb_call_inits(void); | |
| diff --git a/test/ruby/test_fiber.rb b/test/ruby/test_fiber.rb | |
| index 44bdbfc..2c1edf3 100644 | |
| --- a/test/ruby/test_fiber.rb | |
| +++ b/test/ruby/test_fiber.rb | |
| @@ -261,5 +261,69 @@ class TestFiber < Test::Unit::TestCase | |
| assert_equal(0, status.exitstatus, bug5700) | |
| assert_equal(false, status.signaled?, bug5700) | |
| end | |
| + | |
| + def test_ensure | |
| + bug595 = '[ruby-dev:36511]' | |
| + | |
| + assert_in_out_err([], <<-EOS, ["ok"], [], bug595) | |
| + Fiber.new{ | |
| + begin | |
| + Fiber.yield | |
| + ensure | |
| + puts "ok" | |
| + end | |
| + }.resume | |
| + EOS | |
| + | |
| + assert_in_out_err([], <<-EOS, ["ok"] * 1000, [], "[ruby-dev:41035]") | |
| + 1000.times do | |
| + Fiber.new do | |
| + begin | |
| + Fiber.yield | |
| + ensure | |
| + puts "ok" | |
| + end | |
| + end.resume | |
| + GC.start | |
| + end | |
| + EOS | |
| + | |
| + assert_in_out_err([], <<-EOS, ["ok"], [], "[ruby-dev:41035]") | |
| + Thread.new do | |
| + Fiber.new do | |
| + begin | |
| + Fiber.yield | |
| + ensure | |
| + puts "ok" | |
| + end | |
| + end.resume | |
| + end | |
| + sleep 1 | |
| + EOS | |
| + | |
| + assert_normal_exit %q{ | |
| + Fiber.new do | |
| + begin | |
| + Fiber.yield | |
| + ensure | |
| + raise | |
| + end | |
| + end.resume | |
| + } | |
| + | |
| + assert_normal_exit %q{ | |
| + Thread.new do | |
| + Fiber.new do | |
| + begin | |
| + Fiber.yield | |
| + ensure | |
| + raise | |
| + end | |
| + end.resume | |
| + sleep | |
| + end | |
| + sleep 0.1 | |
| + } | |
| + end | |
| end | |
| diff --git a/thread.c b/thread.c | |
| index d68fead..f2443a1 100644 | |
| --- a/thread.c | |
| +++ b/thread.c | |
| @@ -507,6 +507,11 @@ thread_start_func_2(rb_thread_t *th, VALUE *stack_start, VALUE *register_stack_s | |
| /* delete self other than main thread from living_threads */ | |
| if (th != main_th) { | |
| + TH_PUSH_TAG(th); | |
| + if (EXEC_TAG() == 0) { | |
| + rb_thread_terminate_fibers(th); | |
| + } | |
| + TH_POP_TAG(); | |
| st_delete_wrap(th->vm->living_threads, th->self); | |
| } | |
| diff --git a/vm.c b/vm.c | |
| index dd216bd..8aae668 100644 | |
| --- a/vm.c | |
| +++ b/vm.c | |
| @@ -1618,6 +1618,7 @@ void | |
| rb_thread_mark(void *ptr) | |
| { | |
| rb_thread_t *th = NULL; | |
| + VALUE fibval; | |
| RUBY_MARK_ENTER("thread"); | |
| if (ptr) { | |
| th = ptr; | |
| @@ -1661,6 +1662,8 @@ rb_thread_mark(void *ptr) | |
| RUBY_MARK_UNLESS_NULL(th->stat_insn_usage); | |
| RUBY_MARK_UNLESS_NULL(th->last_status); | |
| + rb_fibers_mark(th->root_fiber); | |
| + | |
| RUBY_MARK_UNLESS_NULL(th->locking_mutex); | |
| rb_mark_tbl(th->local_storage); | |
| diff --git a/vm_core.h b/vm_core.h | |
| index 130063a..9fa0efd 100644 | |
| --- a/vm_core.h | |
| +++ b/vm_core.h | |
| @@ -736,6 +736,7 @@ int rb_vm_get_sourceline(const rb_control_frame_t *); | |
| VALUE rb_name_err_mesg_new(VALUE obj, VALUE mesg, VALUE recv, VALUE method); | |
| void rb_vm_stack_to_heap(rb_thread_t *th); | |
| void ruby_thread_init_stack(rb_thread_t *th); | |
| +void rb_thread_terminate_fibers(rb_thread_t *); | |
| NOINLINE(void rb_gc_save_machine_context(rb_thread_t *)); | |
| void rb_gc_mark_machine_stack(rb_thread_t *th); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment