summaryrefslogtreecommitdiff
path: root/cont.c
diff options
context:
space:
mode:
authorko1 <ko1@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2008-10-22 15:12:07 +0000
committerko1 <ko1@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2008-10-22 15:12:07 +0000
commit8a0d53ac2b0d2af06d4c5fef87d1bb5913adbb05 (patch)
tree05c7d9110f8547b1ec14b3b29b3e5000eb3741d8 /cont.c
parentd1c4aefa6296d496781218a3e60e71c4b4a5e2c8 (diff)
* cont.c: separate data structure between rb_fiber_t and
rb_context_t (rb_fiber_t includes rb_context_t). rb_fiber_t has double linked list of fibers in same threads. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@19890 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
Diffstat (limited to 'cont.c')
-rw-r--r--cont.c220
1 files changed, 152 insertions, 68 deletions
diff --git a/cont.c b/cont.c
index ad2c358264..6a4cabffcf 100644
--- a/cont.c
+++ b/cont.c
@@ -21,6 +21,7 @@ enum context_type {
};
typedef struct rb_context_struct {
+ enum context_type type;
VALUE self;
VALUE value;
VALUE *vm_stack;
@@ -34,11 +35,22 @@ typedef struct rb_context_struct {
rb_thread_t saved_thread;
rb_jmpbuf_t jmpbuf;
int machine_stack_size;
- VALUE prev;
- int alive;
- enum context_type type;
} rb_context_t;
+enum fiber_status {
+ CREATED,
+ RUNNING,
+ TERMINATED,
+};
+
+typedef struct rb_fiber_struct {
+ rb_context_t cont;
+ VALUE prev;
+ enum fiber_status status;
+ struct rb_fiber_struct *prev_fiber;
+ struct rb_fiber_struct *next_fiber;
+} rb_fiber_t;
+
static VALUE rb_cContinuation;
static VALUE rb_cFiber;
static VALUE rb_eFiberError;
@@ -46,6 +58,9 @@ static VALUE rb_eFiberError;
#define GetContPtr(obj, ptr) \
Data_Get_Struct(obj, rb_context_t, ptr)
+#define GetFiberPtr(obj, ptr) \
+ Data_Get_Struct(obj, rb_fiber_t, ptr)
+
NOINLINE(static VALUE cont_capture(volatile int *stat));
void rb_thread_mark(rb_thread_t *th);
@@ -57,7 +72,6 @@ cont_mark(void *ptr)
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) {
@@ -85,23 +99,70 @@ 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->saved_thread.stack); fflush(stdout);
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);
- }
-
+ /* free rb_cont_t or rb_fiber_t */
ruby_xfree(ptr);
}
RUBY_FREE_LEAVE("cont");
}
static void
+fiber_mark(void *ptr)
+{
+ RUBY_MARK_ENTER("cont");
+ if (ptr) {
+ rb_fiber_t *fib = ptr;
+ rb_gc_mark(fib->prev);
+ cont_mark(&fib->cont);
+ }
+ RUBY_MARK_LEAVE("cont");
+}
+
+static void
+fiber_link_join(rb_fiber_t *fib)
+{
+ VALUE current_fibval = rb_fiber_current();
+ rb_fiber_t *current_fib;
+ GetFiberPtr(current_fibval, current_fib);
+
+ /* join fiber link */
+ fib->next_fiber = current_fib->next_fiber;
+ fib->prev_fiber = current_fib;
+ current_fib->next_fiber->prev_fiber = fib;
+ current_fib->next_fiber = fib;
+}
+
+static void
+fiber_link_remove(rb_fiber_t *fib)
+{
+ fib->prev_fiber->next_fiber = fib->next_fiber;
+ fib->next_fiber->prev_fiber = fib->prev_fiber;
+}
+
+static void
+fiber_free(void *ptr)
+{
+ RUBY_FREE_ENTER("fiber");
+ if (ptr) {
+ rb_fiber_t *fib = ptr;
+
+ if (fib->cont.type != ROOT_FIBER_CONTEXT) {
+ st_free_table(fib->cont.saved_thread.local_storage);
+ }
+ fiber_link_remove(fib);
+
+ cont_free(&fib->cont);
+ }
+ RUBY_FREE_LEAVE("fiber");
+}
+
+static void
cont_save_machine_stack(rb_thread_t *th, rb_context_t *cont)
{
int size;
@@ -151,21 +212,24 @@ cont_save_machine_stack(rb_thread_t *th, rb_context_t *cont)
#endif
}
+static void
+cont_init(rb_context_t *cont)
+{
+ rb_thread_t *th = GET_THREAD();
+
+ /* save thread context */
+ cont->saved_thread = *th;
+}
+
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;
-
+ cont_init(cont);
return cont;
}
@@ -465,10 +529,6 @@ rb_cont_call(int argc, VALUE *argv, VALUE contval)
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);
@@ -483,25 +543,32 @@ rb_cont_call(int argc, VALUE *argv, VALUE contval)
#define FIBER_VM_STACK_SIZE (4 * 1024)
-static rb_context_t *
+static rb_fiber_t *
fiber_alloc(VALUE klass)
{
- rb_context_t *cont = cont_new(klass);
+ rb_fiber_t *fib;
+ volatile VALUE fibval = Data_Make_Struct(klass, rb_fiber_t, fiber_mark, fiber_free, fib);
- cont->type = FIBER_CONTEXT;
- cont->prev = Qnil;
+ fib->cont.self = fibval;
+ fib->cont.type = FIBER_CONTEXT;
+ cont_init(&fib->cont);
+ fib->prev = Qnil;
+ fib->status = CREATED;
- return cont;
+ return fib;
}
static VALUE
fiber_new(VALUE klass, VALUE proc)
{
- rb_context_t *cont = fiber_alloc(klass);
- VALUE contval = cont->self;
+ rb_fiber_t *fib = fiber_alloc(klass);
+ VALUE fibval = fib->cont.self;
+ rb_context_t *cont = &fib->cont;
rb_thread_t *th = &cont->saved_thread;
- /* initialize */
+ fiber_link_join(fib);
+
+ /* initialize cont */
cont->vm_stack = 0;
th->stack = 0;
@@ -528,7 +595,7 @@ fiber_new(VALUE klass, VALUE proc)
MEMCPY(&cont->jmpbuf, &th->root_jmpbuf, rb_jmpbuf_t, 1);
- return contval;
+ return fibval;
}
VALUE
@@ -546,11 +613,11 @@ rb_fiber_s_new(VALUE self)
static VALUE
return_fiber(void)
{
- rb_context_t *cont;
+ rb_fiber_t *fib;
VALUE curr = rb_fiber_current();
- GetContPtr(curr, cont);
+ GetFiberPtr(curr, fib);
- if (cont->prev == Qnil) {
+ if (fib->prev == Qnil) {
rb_thread_t *th = GET_THREAD();
if (th->root_fiber != curr) {
@@ -561,8 +628,8 @@ return_fiber(void)
}
}
else {
- VALUE prev = cont->prev;
- cont->prev = Qnil;
+ VALUE prev = fib->prev;
+ fib->prev = Qnil;
return prev;
}
}
@@ -570,10 +637,10 @@ return_fiber(void)
VALUE rb_fiber_transfer(VALUE fib, int argc, VALUE *argv);
static void
-rb_fiber_terminate(rb_context_t *cont)
+rb_fiber_terminate(rb_fiber_t *fib)
{
- VALUE value = cont->value;
- cont->alive = Qfalse;
+ VALUE value = fib->cont.value;
+ fib->status = TERMINATED;
rb_fiber_transfer(return_fiber(), 1, &value);
}
@@ -581,12 +648,15 @@ void
rb_fiber_start(void)
{
rb_thread_t *th = GET_THREAD();
+ rb_fiber_t *fib;
rb_context_t *cont;
rb_proc_t *proc;
VALUE args;
int state;
- GetContPtr(th->fiber, cont);
+ GetFiberPtr(th->fiber, fib);
+ cont = &fib->cont;
+
TH_PUSH_TAG(th);
if ((state = EXEC_TAG()) == 0) {
GetProcPtr(cont->saved_thread.first_proc, proc);
@@ -596,6 +666,7 @@ rb_fiber_start(void)
th->local_lfp = proc->block.lfp;
th->local_svar = Qnil;
+ fib->status = RUNNING;
cont->value = vm_invoke_proc(th, proc, proc->block.self, 1, &args, 0);
}
TH_POP_TAG();
@@ -611,46 +682,57 @@ rb_fiber_start(void)
RUBY_VM_SET_INTERRUPT(th);
}
- rb_fiber_terminate(cont);
+ rb_fiber_terminate(fib);
rb_bug("rb_fiber_start: unreachable");
}
+static rb_fiber_t *
+root_fiber_alloc(rb_thread_t *th)
+{
+ rb_fiber_t *fib;
+
+ /* no need to allocate vm stack */
+ fib = fiber_alloc(rb_cFiber);
+ fib->cont.type = ROOT_FIBER_CONTEXT;
+ fib->prev_fiber = fib->next_fiber = fib;
+
+ return fib;
+}
+
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;
+ rb_fiber_t *fib = root_fiber_alloc(th);
+ th->root_fiber = th->fiber = fib->cont.self;
}
return th->fiber;
}
static VALUE
-fiber_store(rb_context_t *next_cont)
+fiber_store(rb_fiber_t *next_fib)
{
rb_thread_t *th = GET_THREAD();
- rb_context_t *cont;
+ rb_fiber_t *fib;
if (th->fiber) {
- GetContPtr(th->fiber, cont);
- cont->saved_thread = *th;
+ GetFiberPtr(th->fiber, fib);
+ fib->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;
+ fib = root_fiber_alloc(th);
+ th->root_fiber = th->fiber = fib->cont.self;
}
- cont_save_machine_stack(th, cont);
+ cont_save_machine_stack(th, &fib->cont);
- if (ruby_setjmp(cont->jmpbuf)) {
+ if (ruby_setjmp(fib->cont.jmpbuf)) {
/* restored */
- GetContPtr(th->fiber, cont);
- return cont->value;
+ GetFiberPtr(th->fiber, fib);
+ return fib->cont.value;
}
else {
return Qundef;
@@ -658,13 +740,15 @@ fiber_store(rb_context_t *next_cont)
}
static inline VALUE
-fiber_switch(VALUE fib, int argc, VALUE *argv, int is_resume)
+fiber_switch(VALUE fibval, int argc, VALUE *argv, int is_resume)
{
VALUE value;
+ rb_fiber_t *fib;
rb_context_t *cont;
rb_thread_t *th = GET_THREAD();
- GetContPtr(fib, cont);
+ GetFiberPtr(fibval, fib);
+ cont = &fib->cont;
if (cont->saved_thread.self != th->self) {
rb_raise(rb_eFiberError, "fiber called across threads");
@@ -672,18 +756,18 @@ fiber_switch(VALUE fib, int argc, VALUE *argv, int is_resume)
else if (cont->saved_thread.trap_tag != th->trap_tag) {
rb_raise(rb_eFiberError, "fiber called across trap");
}
- else if (!cont->alive) {
+ else if (fib->status == TERMINATED) {
rb_raise(rb_eFiberError, "dead fiber called");
}
if (is_resume) {
- cont->prev = rb_fiber_current();
+ fib->prev = rb_fiber_current();
}
cont->value = make_passing_arg(argc, argv);
- if ((value = fiber_store(cont)) == Qundef) {
- cont_restore_0(cont, &value);
+ if ((value = fiber_store(fib)) == Qundef) {
+ cont_restore_0(&fib->cont, &value);
rb_bug("rb_fiber_resume: unreachable");
}
@@ -699,16 +783,16 @@ rb_fiber_transfer(VALUE fib, int argc, VALUE *argv)
}
VALUE
-rb_fiber_resume(VALUE fib, int argc, VALUE *argv)
+rb_fiber_resume(VALUE fibval, int argc, VALUE *argv)
{
- rb_context_t *cont;
- GetContPtr(fib, cont);
+ rb_fiber_t *fib;
+ GetFiberPtr(fibval, fib);
- if (cont->prev != Qnil) {
+ if (fib->prev != Qnil) {
rb_raise(rb_eFiberError, "double resume");
}
- return fiber_switch(fib, argc, argv, 1);
+ return fiber_switch(fibval, argc, argv, 1);
}
VALUE
@@ -718,11 +802,11 @@ rb_fiber_yield(int argc, VALUE *argv)
}
VALUE
-rb_fiber_alive_p(VALUE fib)
+rb_fiber_alive_p(VALUE fibval)
{
- rb_context_t *cont;
- GetContPtr(fib, cont);
- return cont->alive;
+ rb_fiber_t *fib;
+ GetFiberPtr(fibval, fib);
+ return fib->status != TERMINATED;
}
static VALUE