mirror of
https://github.com/ruby/ruby.git
synced 2022-11-09 12:17:21 -05:00
* 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
This commit is contained in:
parent
c4d6f4c01e
commit
c5e4cd0638
6 changed files with 169 additions and 81 deletions
11
ChangeLog
11
ChangeLog
|
@ -1,3 +1,14 @@
|
|||
Wed Aug 22 03:51:07 2007 Koichi Sasada <ko1@atdot.net>
|
||||
|
||||
* 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@fsij.org>
|
||||
|
||||
* lib/tmpdir.rb (Dir.mktmpdir): make directory suffix specifiable.
|
||||
|
|
141
cont.c
141
cont.c
|
@ -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,20 +512,38 @@ rb_fiber_s_new(VALUE self)
|
|||
return contval;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
rb_context_t *prev_cont;
|
||||
VALUE value = cont->value;
|
||||
|
||||
GetContPtr(cont->prev, prev_cont);
|
||||
cont->alive = Qfalse;
|
||||
if (prev_cont->alive == Qfalse) {
|
||||
rb_fiber_yield(GET_THREAD()->root_fiber, 1, &value);
|
||||
}
|
||||
else {
|
||||
rb_fiber_yield(cont->prev, 1, &value);
|
||||
}
|
||||
rb_fiber_transfer(return_fiber(), 1, &value);
|
||||
}
|
||||
|
||||
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
|
||||
|
@ -652,24 +695,30 @@ rb_fiber_alive_p(VALUE fib)
|
|||
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();
|
||||
}
|
||||
|
||||
static VALUE
|
||||
rb_fiber_s_prev(VALUE klass)
|
||||
{
|
||||
return rb_fiber_prev(rb_fiber_s_current(Qnil));
|
||||
}
|
||||
|
||||
static VALUE
|
||||
rb_fiber_s_yield(int argc, VALUE *argv, VALUE fib)
|
||||
{
|
||||
return rb_fiber_yield(rb_fiber_s_prev(Qnil), argc, argv);
|
||||
}
|
||||
|
||||
void
|
||||
Init_Cont(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);
|
||||
}
|
||||
|
||||
|
|
|
@ -373,7 +373,7 @@ next_ii(VALUE i, VALUE obj)
|
|||
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 @@ next_i(VALUE curr, VALUE obj)
|
|||
|
||||
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 @@ next_init(VALUE obj, struct enumerator *e)
|
|||
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 @@ enumerator_next(VALUE obj)
|
|||
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;
|
||||
}
|
||||
|
||||
|
|
|
@ -148,7 +148,8 @@ VALUE rb_singleton_class(VALUE);
|
|||
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 */
|
||||
|
|
|
@ -6,25 +6,24 @@ class TestFiber < Test::Unit::TestCase
|
|||
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 @@ class TestFiber < Test::Unit::TestCase
|
|||
assert_equal(max,
|
||||
max.times{|i|
|
||||
Fiber.new{
|
||||
}.yield
|
||||
}.resume
|
||||
}
|
||||
)
|
||||
end
|
||||
|
@ -48,7 +47,7 @@ class TestFiber < Test::Unit::TestCase
|
|||
max.times{|i|
|
||||
Fiber.new{
|
||||
@cnt += 1
|
||||
}.yield
|
||||
}.resume
|
||||
}
|
||||
}
|
||||
}.each{|t|
|
||||
|
@ -63,50 +62,72 @@ class TestFiber < Test::Unit::TestCase
|
|||
}
|
||||
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_raise(FiberError){
|
||||
fib = Fiber.new{
|
||||
fib.resume
|
||||
}
|
||||
fib.resume
|
||||
}
|
||||
assert_raise(FiberError){
|
||||
fib = Fiber.new{
|
||||
Fiber.new{
|
||||
fib.resume
|
||||
}.resume
|
||||
}
|
||||
fib.resume
|
||||
}
|
||||
assert_equal(:ok, f1.yield)
|
||||
assert_equal([:baz, :bar], ary)
|
||||
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
|
||||
|
||||
|
|
|
@ -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[];
|
||||
|
|
Loading…
Reference in a new issue