cont.c

Go to the documentation of this file.
00001 /**********************************************************************
00002 
00003   cont.c -
00004 
00005   $Author: nagachika $
00006   created at: Thu May 23 09:03:43 2007
00007 
00008   Copyright (C) 2007 Koichi Sasada
00009 
00010 **********************************************************************/
00011 
00012 #include "ruby/ruby.h"
00013 #include "internal.h"
00014 #include "vm_core.h"
00015 #include "gc.h"
00016 #include "eval_intern.h"
00017 
00018 /* FIBER_USE_NATIVE enables Fiber performance improvement using system
00019  * dependent method such as make/setcontext on POSIX system or
00020  * CreateFiber() API on Windows.
00021  * This hack make Fiber context switch faster (x2 or more).
00022  * However, it decrease maximum number of Fiber.  For example, on the
00023  * 32bit POSIX OS, ten or twenty thousands Fiber can be created.
00024  *
00025  * Details is reported in the paper "A Fast Fiber Implementation for Ruby 1.9"
00026  * in Proc. of 51th Programming Symposium, pp.21--28 (2010) (in Japanese).
00027  */
00028 
00029 #if !defined(FIBER_USE_NATIVE)
00030 # if defined(HAVE_GETCONTEXT) && defined(HAVE_SETCONTEXT)
00031 #   if 0
00032 #   elif defined(__NetBSD__)
00033 /* On our experience, NetBSD doesn't support using setcontext() and pthread
00034  * simultaneously.  This is because pthread_self(), TLS and other information
00035  * are represented by stack pointer (higher bits of stack pointer).
00036  * TODO: check such constraint on configure.
00037  */
00038 #     define FIBER_USE_NATIVE 0
00039 #   elif defined(__sun)
00040 /* On Solaris because resuming any Fiber caused SEGV, for some reason.
00041  */
00042 #     define FIBER_USE_NATIVE 0
00043 #   elif defined(__ia64)
00044 /* At least, Linux/ia64's getcontext(3) doesn't save register window.
00045  */
00046 #     define FIBER_USE_NATIVE 0
00047 #   elif defined(__GNU__)
00048 /* GNU/Hurd doesn't fully support getcontext, setcontext, makecontext
00049  * and swapcontext functions. Disabling their usage till support is
00050  * implemented. More info at
00051  * http://darnassus.sceen.net/~hurd-web/open_issues/glibc/#getcontext
00052  */
00053 #     define FIBER_USE_NATIVE 0
00054 #   else
00055 #     define FIBER_USE_NATIVE 1
00056 #   endif
00057 # elif defined(_WIN32)
00058 #   if _WIN32_WINNT >= 0x0400
00059 /* only when _WIN32_WINNT >= 0x0400 on Windows because Fiber APIs are
00060  * supported only such building (and running) environments.
00061  * [ruby-dev:41192]
00062  */
00063 #     define FIBER_USE_NATIVE 1
00064 #   endif
00065 # endif
00066 #endif
00067 #if !defined(FIBER_USE_NATIVE)
00068 #define FIBER_USE_NATIVE 0
00069 #endif
00070 
00071 #if FIBER_USE_NATIVE
00072 #ifndef _WIN32
00073 #include <unistd.h>
00074 #include <sys/mman.h>
00075 #include <ucontext.h>
00076 #endif
00077 #define RB_PAGE_SIZE (pagesize)
00078 #define RB_PAGE_MASK (~(RB_PAGE_SIZE - 1))
00079 static long pagesize;
00080 #endif /*FIBER_USE_NATIVE*/
00081 
00082 #define CAPTURE_JUST_VALID_VM_STACK 1
00083 
00084 enum context_type {
00085     CONTINUATION_CONTEXT = 0,
00086     FIBER_CONTEXT = 1,
00087     ROOT_FIBER_CONTEXT = 2
00088 };
00089 
00090 typedef struct rb_context_struct {
00091     enum context_type type;
00092     VALUE self;
00093     int argc;
00094     VALUE value;
00095     VALUE *vm_stack;
00096 #ifdef CAPTURE_JUST_VALID_VM_STACK
00097     size_t vm_stack_slen;  /* length of stack (head of th->stack) */
00098     size_t vm_stack_clen;  /* length of control frames (tail of th->stack) */
00099 #endif
00100     struct {
00101         VALUE *stack;
00102         VALUE *stack_src;
00103         size_t stack_size;
00104 #ifdef __ia64
00105         VALUE *register_stack;
00106         VALUE *register_stack_src;
00107         int register_stack_size;
00108 #endif
00109     } machine;
00110     rb_thread_t saved_thread;
00111     rb_jmpbuf_t jmpbuf;
00112     rb_ensure_entry_t *ensure_array;
00113     rb_ensure_list_t *ensure_list;
00114 } rb_context_t;
00115 
00116 enum fiber_status {
00117     CREATED,
00118     RUNNING,
00119     TERMINATED
00120 };
00121 
00122 #if FIBER_USE_NATIVE && !defined(_WIN32)
00123 #define MAX_MACHINE_STACK_CACHE  10
00124 static int machine_stack_cache_index = 0;
00125 typedef struct machine_stack_cache_struct {
00126     void *ptr;
00127     size_t size;
00128 } machine_stack_cache_t;
00129 static machine_stack_cache_t machine_stack_cache[MAX_MACHINE_STACK_CACHE];
00130 static machine_stack_cache_t terminated_machine_stack;
00131 #endif
00132 
00133 typedef struct rb_fiber_struct {
00134     rb_context_t cont;
00135     VALUE prev;
00136     enum fiber_status status;
00137     struct rb_fiber_struct *prev_fiber;
00138     struct rb_fiber_struct *next_fiber;
00139     /* If a fiber invokes "transfer",
00140      * then this fiber can't "resume" any more after that.
00141      * You shouldn't mix "transfer" and "resume".
00142      */
00143     int transfered;
00144 
00145 #if FIBER_USE_NATIVE
00146 #ifdef _WIN32
00147     void *fib_handle;
00148 #else
00149     ucontext_t context;
00150 #endif
00151 #endif
00152 } rb_fiber_t;
00153 
00154 static const rb_data_type_t cont_data_type, fiber_data_type;
00155 static VALUE rb_cContinuation;
00156 static VALUE rb_cFiber;
00157 static VALUE rb_eFiberError;
00158 
00159 #define GetContPtr(obj, ptr)  \
00160     TypedData_Get_Struct((obj), rb_context_t, &cont_data_type, (ptr))
00161 
00162 #define GetFiberPtr(obj, ptr)  do {\
00163     TypedData_Get_Struct((obj), rb_fiber_t, &fiber_data_type, (ptr)); \
00164     if (!(ptr)) rb_raise(rb_eFiberError, "uninitialized fiber"); \
00165 } while (0)
00166 
00167 NOINLINE(static VALUE cont_capture(volatile int *stat));
00168 
00169 #define THREAD_MUST_BE_RUNNING(th) do { \
00170         if (!(th)->tag) rb_raise(rb_eThreadError, "not running thread");        \
00171     } while (0)
00172 
00173 static void
00174 cont_mark(void *ptr)
00175 {
00176     RUBY_MARK_ENTER("cont");
00177     if (ptr) {
00178         rb_context_t *cont = ptr;
00179         rb_gc_mark(cont->value);
00180         rb_thread_mark(&cont->saved_thread);
00181         rb_gc_mark(cont->saved_thread.self);
00182 
00183         if (cont->vm_stack) {
00184 #ifdef CAPTURE_JUST_VALID_VM_STACK
00185             rb_gc_mark_locations(cont->vm_stack,
00186                                  cont->vm_stack + cont->vm_stack_slen + cont->vm_stack_clen);
00187 #else
00188             rb_gc_mark_localtion(cont->vm_stack,
00189                                  cont->vm_stack, cont->saved_thread.stack_size);
00190 #endif
00191         }
00192 
00193         if (cont->machine.stack) {
00194             if (cont->type == CONTINUATION_CONTEXT) {
00195                 /* cont */
00196                 rb_gc_mark_locations(cont->machine.stack,
00197                                      cont->machine.stack + cont->machine.stack_size);
00198             }
00199             else {
00200                 /* fiber */
00201                 rb_thread_t *th;
00202                 rb_fiber_t *fib = (rb_fiber_t*)cont;
00203                 GetThreadPtr(cont->saved_thread.self, th);
00204                 if ((th->fiber != cont->self) && fib->status == RUNNING) {
00205                     rb_gc_mark_locations(cont->machine.stack,
00206                                          cont->machine.stack + cont->machine.stack_size);
00207                 }
00208             }
00209         }
00210 #ifdef __ia64
00211         if (cont->machine.register_stack) {
00212             rb_gc_mark_locations(cont->machine.register_stack,
00213                                  cont->machine.register_stack + cont->machine.register_stack_size);
00214         }
00215 #endif
00216     }
00217     RUBY_MARK_LEAVE("cont");
00218 }
00219 
00220 static void
00221 cont_free(void *ptr)
00222 {
00223     RUBY_FREE_ENTER("cont");
00224     if (ptr) {
00225         rb_context_t *cont = ptr;
00226         RUBY_FREE_UNLESS_NULL(cont->saved_thread.stack); fflush(stdout);
00227 #if FIBER_USE_NATIVE
00228         if (cont->type == CONTINUATION_CONTEXT) {
00229             /* cont */
00230             ruby_xfree(cont->ensure_array);
00231             RUBY_FREE_UNLESS_NULL(cont->machine.stack);
00232         }
00233         else {
00234             /* fiber */
00235 #ifdef _WIN32
00236             if (GET_THREAD()->fiber != cont->self && cont->type != ROOT_FIBER_CONTEXT) {
00237                 /* don't delete root fiber handle */
00238                 rb_fiber_t *fib = (rb_fiber_t*)cont;
00239                 if (fib->fib_handle) {
00240                     DeleteFiber(fib->fib_handle);
00241                 }
00242             }
00243 #else /* not WIN32 */
00244             if (GET_THREAD()->fiber != cont->self) {
00245                 rb_fiber_t *fib = (rb_fiber_t*)cont;
00246                 if (fib->context.uc_stack.ss_sp) {
00247                     if (cont->type == ROOT_FIBER_CONTEXT) {
00248                         rb_bug("Illegal root fiber parameter");
00249                     }
00250                     munmap((void*)fib->context.uc_stack.ss_sp, fib->context.uc_stack.ss_size);
00251                 }
00252             }
00253             else {
00254                 /* It may reached here when finalize */
00255                 /* TODO examine whether it is a bug */
00256                 /* rb_bug("cont_free: release self"); */
00257             }
00258 #endif
00259         }
00260 #else /* not FIBER_USE_NATIVE */
00261         ruby_xfree(cont->ensure_array);
00262         RUBY_FREE_UNLESS_NULL(cont->machine.stack);
00263 #endif
00264 #ifdef __ia64
00265         RUBY_FREE_UNLESS_NULL(cont->machine.register_stack);
00266 #endif
00267         RUBY_FREE_UNLESS_NULL(cont->vm_stack);
00268 
00269         /* free rb_cont_t or rb_fiber_t */
00270         ruby_xfree(ptr);
00271     }
00272     RUBY_FREE_LEAVE("cont");
00273 }
00274 
00275 static size_t
00276 cont_memsize(const void *ptr)
00277 {
00278     const rb_context_t *cont = ptr;
00279     size_t size = 0;
00280     if (cont) {
00281         size = sizeof(*cont);
00282         if (cont->vm_stack) {
00283 #ifdef CAPTURE_JUST_VALID_VM_STACK
00284             size_t n = (cont->vm_stack_slen + cont->vm_stack_clen);
00285 #else
00286             size_t n = cont->saved_thread.stack_size;
00287 #endif
00288             size += n * sizeof(*cont->vm_stack);
00289         }
00290 
00291         if (cont->machine.stack) {
00292             size += cont->machine.stack_size * sizeof(*cont->machine.stack);
00293         }
00294 #ifdef __ia64
00295         if (cont->machine.register_stack) {
00296             size += cont->machine.register_stack_size * sizeof(*cont->machine.register_stack);
00297         }
00298 #endif
00299     }
00300     return size;
00301 }
00302 
00303 static void
00304 fiber_mark(void *ptr)
00305 {
00306     RUBY_MARK_ENTER("cont");
00307     if (ptr) {
00308         rb_fiber_t *fib = ptr;
00309         rb_gc_mark(fib->prev);
00310         cont_mark(&fib->cont);
00311     }
00312     RUBY_MARK_LEAVE("cont");
00313 }
00314 
00315 static void
00316 fiber_link_join(rb_fiber_t *fib)
00317 {
00318     VALUE current_fibval = rb_fiber_current();
00319     rb_fiber_t *current_fib;
00320     GetFiberPtr(current_fibval, current_fib);
00321 
00322     /* join fiber link */
00323     fib->next_fiber = current_fib->next_fiber;
00324     fib->prev_fiber = current_fib;
00325     current_fib->next_fiber->prev_fiber = fib;
00326     current_fib->next_fiber = fib;
00327 }
00328 
00329 static void
00330 fiber_link_remove(rb_fiber_t *fib)
00331 {
00332     fib->prev_fiber->next_fiber = fib->next_fiber;
00333     fib->next_fiber->prev_fiber = fib->prev_fiber;
00334 }
00335 
00336 static void
00337 fiber_free(void *ptr)
00338 {
00339     RUBY_FREE_ENTER("fiber");
00340     if (ptr) {
00341         rb_fiber_t *fib = ptr;
00342         if (fib->cont.type != ROOT_FIBER_CONTEXT &&
00343             fib->cont.saved_thread.local_storage) {
00344             st_free_table(fib->cont.saved_thread.local_storage);
00345         }
00346         fiber_link_remove(fib);
00347 
00348         cont_free(&fib->cont);
00349     }
00350     RUBY_FREE_LEAVE("fiber");
00351 }
00352 
00353 static size_t
00354 fiber_memsize(const void *ptr)
00355 {
00356     const rb_fiber_t *fib = ptr;
00357     size_t size = 0;
00358     if (ptr) {
00359         size = sizeof(*fib);
00360         if (fib->cont.type != ROOT_FIBER_CONTEXT &&
00361             fib->cont.saved_thread.local_storage != NULL) {
00362             size += st_memsize(fib->cont.saved_thread.local_storage);
00363         }
00364         size += cont_memsize(&fib->cont);
00365     }
00366     return size;
00367 }
00368 
00369 VALUE
00370 rb_obj_is_fiber(VALUE obj)
00371 {
00372     if (rb_typeddata_is_kind_of(obj, &fiber_data_type)) {
00373         return Qtrue;
00374     }
00375     else {
00376         return Qfalse;
00377     }
00378 }
00379 
00380 static void
00381 cont_save_machine_stack(rb_thread_t *th, rb_context_t *cont)
00382 {
00383     size_t size;
00384 
00385     SET_MACHINE_STACK_END(&th->machine.stack_end);
00386 #ifdef __ia64
00387     th->machine.register_stack_end = rb_ia64_bsp();
00388 #endif
00389 
00390     if (th->machine.stack_start > th->machine.stack_end) {
00391         size = cont->machine.stack_size = th->machine.stack_start - th->machine.stack_end;
00392         cont->machine.stack_src = th->machine.stack_end;
00393     }
00394     else {
00395         size = cont->machine.stack_size = th->machine.stack_end - th->machine.stack_start;
00396         cont->machine.stack_src = th->machine.stack_start;
00397     }
00398 
00399     if (cont->machine.stack) {
00400         REALLOC_N(cont->machine.stack, VALUE, size);
00401     }
00402     else {
00403         cont->machine.stack = ALLOC_N(VALUE, size);
00404     }
00405 
00406     FLUSH_REGISTER_WINDOWS;
00407     MEMCPY(cont->machine.stack, cont->machine.stack_src, VALUE, size);
00408 
00409 #ifdef __ia64
00410     rb_ia64_flushrs();
00411     size = cont->machine.register_stack_size = th->machine.register_stack_end - th->machine.register_stack_start;
00412     cont->machine.register_stack_src = th->machine.register_stack_start;
00413     if (cont->machine.register_stack) {
00414         REALLOC_N(cont->machine.register_stack, VALUE, size);
00415     }
00416     else {
00417         cont->machine.register_stack = ALLOC_N(VALUE, size);
00418     }
00419 
00420     MEMCPY(cont->machine.register_stack, cont->machine.register_stack_src, VALUE, size);
00421 #endif
00422 }
00423 
00424 static const rb_data_type_t cont_data_type = {
00425     "continuation",
00426     {cont_mark, cont_free, cont_memsize,},
00427     NULL, NULL, RUBY_TYPED_FREE_IMMEDIATELY
00428 };
00429 
00430 static void
00431 cont_save_thread(rb_context_t *cont, rb_thread_t *th)
00432 {
00433     /* save thread context */
00434     cont->saved_thread = *th;
00435     /* saved_thread->machine.stack_(start|end) should be NULL */
00436     /* because it may happen GC afterward */
00437     cont->saved_thread.machine.stack_start = 0;
00438     cont->saved_thread.machine.stack_end = 0;
00439 #ifdef __ia64
00440     cont->saved_thread.machine.register_stack_start = 0;
00441     cont->saved_thread.machine.register_stack_end = 0;
00442 #endif
00443 }
00444 
00445 static void
00446 cont_init(rb_context_t *cont, rb_thread_t *th)
00447 {
00448     /* save thread context */
00449     cont_save_thread(cont, th);
00450     cont->saved_thread.local_storage = 0;
00451 }
00452 
00453 static rb_context_t *
00454 cont_new(VALUE klass)
00455 {
00456     rb_context_t *cont;
00457     volatile VALUE contval;
00458     rb_thread_t *th = GET_THREAD();
00459 
00460     THREAD_MUST_BE_RUNNING(th);
00461     contval = TypedData_Make_Struct(klass, rb_context_t, &cont_data_type, cont);
00462     cont->self = contval;
00463     cont_init(cont, th);
00464     return cont;
00465 }
00466 
00467 static VALUE
00468 cont_capture(volatile int *stat)
00469 {
00470     rb_context_t *cont;
00471     rb_thread_t *th = GET_THREAD(), *sth;
00472     volatile VALUE contval;
00473 
00474     THREAD_MUST_BE_RUNNING(th);
00475     rb_vm_stack_to_heap(th);
00476     cont = cont_new(rb_cContinuation);
00477     contval = cont->self;
00478     sth = &cont->saved_thread;
00479 
00480 #ifdef CAPTURE_JUST_VALID_VM_STACK
00481     cont->vm_stack_slen = th->cfp->sp + th->mark_stack_len - th->stack;
00482     cont->vm_stack_clen = th->stack + th->stack_size - (VALUE*)th->cfp;
00483     cont->vm_stack = ALLOC_N(VALUE, cont->vm_stack_slen + cont->vm_stack_clen);
00484     MEMCPY(cont->vm_stack, th->stack, VALUE, cont->vm_stack_slen);
00485     MEMCPY(cont->vm_stack + cont->vm_stack_slen, (VALUE*)th->cfp, VALUE, cont->vm_stack_clen);
00486 #else
00487     cont->vm_stack = ALLOC_N(VALUE, th->stack_size);
00488     MEMCPY(cont->vm_stack, th->stack, VALUE, th->stack_size);
00489 #endif
00490     sth->stack = 0;
00491 
00492     cont_save_machine_stack(th, cont);
00493 
00494     /* backup ensure_list to array for search in another context */
00495     {
00496         rb_ensure_list_t *p;
00497         int size = 0;
00498         rb_ensure_entry_t *entry;
00499         for (p=th->ensure_list; p; p=p->next)
00500             size++;
00501         entry = cont->ensure_array = ALLOC_N(rb_ensure_entry_t,size+1);
00502         for (p=th->ensure_list; p; p=p->next) {
00503             if (!p->entry.marker)
00504                 p->entry.marker = rb_ary_tmp_new(0); /* dummy object */
00505             *entry++ = p->entry;
00506         }
00507         entry->marker = 0;
00508     }
00509 
00510     if (ruby_setjmp(cont->jmpbuf)) {
00511         volatile VALUE value;
00512 
00513         value = cont->value;
00514         if (cont->argc == -1) rb_exc_raise(value);
00515         cont->value = Qnil;
00516         *stat = 1;
00517         return value;
00518     }
00519     else {
00520         *stat = 0;
00521         return contval;
00522     }
00523 }
00524 
00525 static void
00526 cont_restore_thread(rb_context_t *cont)
00527 {
00528     rb_thread_t *th = GET_THREAD(), *sth = &cont->saved_thread;
00529 
00530     /* restore thread context */
00531     if (cont->type == CONTINUATION_CONTEXT) {
00532         /* continuation */
00533         VALUE fib;
00534 
00535         th->fiber = sth->fiber;
00536         fib = th->fiber ? th->fiber : th->root_fiber;
00537 
00538         if (fib) {
00539             rb_fiber_t *fcont;
00540             GetFiberPtr(fib, fcont);
00541             th->stack_size = fcont->cont.saved_thread.stack_size;
00542             th->stack = fcont->cont.saved_thread.stack;
00543         }
00544 #ifdef CAPTURE_JUST_VALID_VM_STACK
00545         MEMCPY(th->stack, cont->vm_stack, VALUE, cont->vm_stack_slen);
00546         MEMCPY(th->stack + sth->stack_size - cont->vm_stack_clen,
00547                cont->vm_stack + cont->vm_stack_slen, VALUE, cont->vm_stack_clen);
00548 #else
00549         MEMCPY(th->stack, cont->vm_stack, VALUE, sth->stack_size);
00550 #endif
00551     }
00552     else {
00553         /* fiber */
00554         th->stack = sth->stack;
00555         th->stack_size = sth->stack_size;
00556         th->local_storage = sth->local_storage;
00557         th->fiber = cont->self;
00558     }
00559 
00560     th->cfp = sth->cfp;
00561     th->safe_level = sth->safe_level;
00562     th->raised_flag = sth->raised_flag;
00563     th->state = sth->state;
00564     th->status = sth->status;
00565     th->tag = sth->tag;
00566     th->protect_tag = sth->protect_tag;
00567     th->errinfo = sth->errinfo;
00568     th->first_proc = sth->first_proc;
00569     th->root_lep = sth->root_lep;
00570     th->root_svar = sth->root_svar;
00571     th->ensure_list = sth->ensure_list;
00572 
00573 }
00574 
00575 #if FIBER_USE_NATIVE
00576 #ifdef _WIN32
00577 static void
00578 fiber_set_stack_location(void)
00579 {
00580     rb_thread_t *th = GET_THREAD();
00581     VALUE *ptr;
00582 
00583     SET_MACHINE_STACK_END(&ptr);
00584     th->machine.stack_start = (void*)(((VALUE)ptr & RB_PAGE_MASK) + STACK_UPPER((void *)&ptr, 0, RB_PAGE_SIZE));
00585 }
00586 
00587 static VOID CALLBACK
00588 fiber_entry(void *arg)
00589 {
00590     fiber_set_stack_location();
00591     rb_fiber_start();
00592 }
00593 #else /* _WIN32 */
00594 
00595 /*
00596  * FreeBSD require a first (i.e. addr) argument of mmap(2) is not NULL
00597  * if MAP_STACK is passed.
00598  * http://www.FreeBSD.org/cgi/query-pr.cgi?pr=158755
00599  */
00600 #if defined(MAP_STACK) && !defined(__FreeBSD__) && !defined(__FreeBSD_kernel__)
00601 #define FIBER_STACK_FLAGS (MAP_PRIVATE | MAP_ANON | MAP_STACK)
00602 #else
00603 #define FIBER_STACK_FLAGS (MAP_PRIVATE | MAP_ANON)
00604 #endif
00605 
00606 static char*
00607 fiber_machine_stack_alloc(size_t size)
00608 {
00609     char *ptr;
00610 
00611     if (machine_stack_cache_index > 0) {
00612         if (machine_stack_cache[machine_stack_cache_index - 1].size == (size / sizeof(VALUE))) {
00613             ptr = machine_stack_cache[machine_stack_cache_index - 1].ptr;
00614             machine_stack_cache_index--;
00615             machine_stack_cache[machine_stack_cache_index].ptr = NULL;
00616             machine_stack_cache[machine_stack_cache_index].size = 0;
00617         }
00618         else{
00619             /* TODO handle multiple machine stack size */
00620             rb_bug("machine_stack_cache size is not canonicalized");
00621         }
00622     }
00623     else {
00624         void *page;
00625         STACK_GROW_DIR_DETECTION;
00626 
00627         errno = 0;
00628         ptr = mmap(NULL, size, PROT_READ | PROT_WRITE, FIBER_STACK_FLAGS, -1, 0);
00629         if (ptr == MAP_FAILED) {
00630             rb_raise(rb_eFiberError, "can't alloc machine stack to fiber: %s", strerror(errno));
00631         }
00632 
00633         /* guard page setup */
00634         page = ptr + STACK_DIR_UPPER(size - RB_PAGE_SIZE, 0);
00635         if (mprotect(page, RB_PAGE_SIZE, PROT_NONE) < 0) {
00636             rb_raise(rb_eFiberError, "mprotect failed");
00637         }
00638     }
00639 
00640     return ptr;
00641 }
00642 #endif
00643 
00644 static void
00645 fiber_initialize_machine_stack_context(rb_fiber_t *fib, size_t size)
00646 {
00647     rb_thread_t *sth = &fib->cont.saved_thread;
00648 
00649 #ifdef _WIN32
00650     fib->fib_handle = CreateFiberEx(size - 1, size, 0, fiber_entry, NULL);
00651     if (!fib->fib_handle) {
00652         /* try to release unnecessary fibers & retry to create */
00653         rb_gc();
00654         fib->fib_handle = CreateFiberEx(size - 1, size, 0, fiber_entry, NULL);
00655         if (!fib->fib_handle) {
00656             rb_raise(rb_eFiberError, "can't create fiber");
00657         }
00658     }
00659     sth->machine.stack_maxsize = size;
00660 #else /* not WIN32 */
00661     ucontext_t *context = &fib->context;
00662     char *ptr;
00663     STACK_GROW_DIR_DETECTION;
00664 
00665     getcontext(context);
00666     ptr = fiber_machine_stack_alloc(size);
00667     context->uc_link = NULL;
00668     context->uc_stack.ss_sp = ptr;
00669     context->uc_stack.ss_size = size;
00670     makecontext(context, rb_fiber_start, 0);
00671     sth->machine.stack_start = (VALUE*)(ptr + STACK_DIR_UPPER(0, size));
00672     sth->machine.stack_maxsize = size - RB_PAGE_SIZE;
00673 #endif
00674 #ifdef __ia64
00675     sth->machine.register_stack_maxsize = sth->machine.stack_maxsize;
00676 #endif
00677 }
00678 
00679 NOINLINE(static void fiber_setcontext(rb_fiber_t *newfib, rb_fiber_t *oldfib));
00680 
00681 static void
00682 fiber_setcontext(rb_fiber_t *newfib, rb_fiber_t *oldfib)
00683 {
00684     rb_thread_t *th = GET_THREAD(), *sth = &newfib->cont.saved_thread;
00685 
00686     if (newfib->status != RUNNING) {
00687         fiber_initialize_machine_stack_context(newfib, th->vm->default_params.fiber_machine_stack_size);
00688     }
00689 
00690     /* restore thread context */
00691     cont_restore_thread(&newfib->cont);
00692     th->machine.stack_maxsize = sth->machine.stack_maxsize;
00693     if (sth->machine.stack_end && (newfib != oldfib)) {
00694         rb_bug("fiber_setcontext: sth->machine.stack_end has non zero value");
00695     }
00696 
00697     /* save  oldfib's machine stack */
00698     if (oldfib->status != TERMINATED) {
00699         STACK_GROW_DIR_DETECTION;
00700         SET_MACHINE_STACK_END(&th->machine.stack_end);
00701         if (STACK_DIR_UPPER(0, 1)) {
00702             oldfib->cont.machine.stack_size = th->machine.stack_start - th->machine.stack_end;
00703             oldfib->cont.machine.stack = th->machine.stack_end;
00704         }
00705         else {
00706             oldfib->cont.machine.stack_size = th->machine.stack_end - th->machine.stack_start;
00707             oldfib->cont.machine.stack = th->machine.stack_start;
00708         }
00709     }
00710     /* exchange machine_stack_start between oldfib and newfib */
00711     oldfib->cont.saved_thread.machine.stack_start = th->machine.stack_start;
00712     th->machine.stack_start = sth->machine.stack_start;
00713     /* oldfib->machine.stack_end should be NULL */
00714     oldfib->cont.saved_thread.machine.stack_end = 0;
00715 #ifndef _WIN32
00716     if (!newfib->context.uc_stack.ss_sp && th->root_fiber != newfib->cont.self) {
00717         rb_bug("non_root_fiber->context.uc_stac.ss_sp should not be NULL");
00718     }
00719 #endif
00720 
00721     /* swap machine context */
00722 #ifdef _WIN32
00723     SwitchToFiber(newfib->fib_handle);
00724 #else
00725     swapcontext(&oldfib->context, &newfib->context);
00726 #endif
00727 }
00728 #endif
00729 
00730 NOINLINE(NORETURN(static void cont_restore_1(rb_context_t *)));
00731 
00732 static void
00733 cont_restore_1(rb_context_t *cont)
00734 {
00735     cont_restore_thread(cont);
00736 
00737     /* restore machine stack */
00738 #ifdef _M_AMD64
00739     {
00740         /* workaround for x64 SEH */
00741         jmp_buf buf;
00742         setjmp(buf);
00743         ((_JUMP_BUFFER*)(&cont->jmpbuf))->Frame =
00744             ((_JUMP_BUFFER*)(&buf))->Frame;
00745     }
00746 #endif
00747     if (cont->machine.stack_src) {
00748         FLUSH_REGISTER_WINDOWS;
00749         MEMCPY(cont->machine.stack_src, cont->machine.stack,
00750                 VALUE, cont->machine.stack_size);
00751     }
00752 
00753 #ifdef __ia64
00754     if (cont->machine.register_stack_src) {
00755         MEMCPY(cont->machine.register_stack_src, cont->machine.register_stack,
00756                VALUE, cont->machine.register_stack_size);
00757     }
00758 #endif
00759 
00760     ruby_longjmp(cont->jmpbuf, 1);
00761 }
00762 
00763 NORETURN(NOINLINE(static void cont_restore_0(rb_context_t *, VALUE *)));
00764 
00765 #ifdef __ia64
00766 #define C(a) rse_##a##0, rse_##a##1, rse_##a##2, rse_##a##3, rse_##a##4
00767 #define E(a) rse_##a##0= rse_##a##1= rse_##a##2= rse_##a##3= rse_##a##4
00768 static volatile int C(a), C(b), C(c), C(d), C(e);
00769 static volatile int C(f), C(g), C(h), C(i), C(j);
00770 static volatile int C(k), C(l), C(m), C(n), C(o);
00771 static volatile int C(p), C(q), C(r), C(s), C(t);
00772 #if 0
00773 {/* the above lines make cc-mode.el confused so much */}
00774 #endif
00775 int rb_dummy_false = 0;
00776 NORETURN(NOINLINE(static void register_stack_extend(rb_context_t *, VALUE *, VALUE *)));
00777 static void
00778 register_stack_extend(rb_context_t *cont, VALUE *vp, VALUE *curr_bsp)
00779 {
00780     if (rb_dummy_false) {
00781         /* use registers as much as possible */
00782         E(a) = E(b) = E(c) = E(d) = E(e) =
00783         E(f) = E(g) = E(h) = E(i) = E(j) =
00784         E(k) = E(l) = E(m) = E(n) = E(o) =
00785         E(p) = E(q) = E(r) = E(s) = E(t) = 0;
00786         E(a) = E(b) = E(c) = E(d) = E(e) =
00787         E(f) = E(g) = E(h) = E(i) = E(j) =
00788         E(k) = E(l) = E(m) = E(n) = E(o) =
00789         E(p) = E(q) = E(r) = E(s) = E(t) = 0;
00790     }
00791     if (curr_bsp < cont->machine.register_stack_src+cont->machine.register_stack_size) {
00792         register_stack_extend(cont, vp, (VALUE*)rb_ia64_bsp());
00793     }
00794     cont_restore_0(cont, vp);
00795 }
00796 #undef C
00797 #undef E
00798 #endif
00799 
00800 static void
00801 cont_restore_0(rb_context_t *cont, VALUE *addr_in_prev_frame)
00802 {
00803     if (cont->machine.stack_src) {
00804 #ifdef HAVE_ALLOCA
00805 #define STACK_PAD_SIZE 1
00806 #else
00807 #define STACK_PAD_SIZE 1024
00808 #endif
00809         VALUE space[STACK_PAD_SIZE];
00810 
00811 #if !STACK_GROW_DIRECTION
00812         if (addr_in_prev_frame > &space[0]) {
00813             /* Stack grows downward */
00814 #endif
00815 #if STACK_GROW_DIRECTION <= 0
00816             volatile VALUE *const end = cont->machine.stack_src;
00817             if (&space[0] > end) {
00818 # ifdef HAVE_ALLOCA
00819                 volatile VALUE *sp = ALLOCA_N(VALUE, &space[0] - end);
00820                 space[0] = *sp;
00821 # else
00822                 cont_restore_0(cont, &space[0]);
00823 # endif
00824             }
00825 #endif
00826 #if !STACK_GROW_DIRECTION
00827         }
00828         else {
00829             /* Stack grows upward */
00830 #endif
00831 #if STACK_GROW_DIRECTION >= 0
00832             volatile VALUE *const end = cont->machine.stack_src + cont->machine.stack_size;
00833             if (&space[STACK_PAD_SIZE] < end) {
00834 # ifdef HAVE_ALLOCA
00835                 volatile VALUE *sp = ALLOCA_N(VALUE, end - &space[STACK_PAD_SIZE]);
00836                 space[0] = *sp;
00837 # else
00838                 cont_restore_0(cont, &space[STACK_PAD_SIZE-1]);
00839 # endif
00840             }
00841 #endif
00842 #if !STACK_GROW_DIRECTION
00843         }
00844 #endif
00845     }
00846     cont_restore_1(cont);
00847 }
00848 #ifdef __ia64
00849 #define cont_restore_0(cont, vp) register_stack_extend((cont), (vp), (VALUE*)rb_ia64_bsp())
00850 #endif
00851 
00852 /*
00853  *  Document-class: Continuation
00854  *
00855  *  Continuation objects are generated by Kernel#callcc,
00856  *  after having +require+d <i>continuation</i>. They hold
00857  *  a return address and execution context, allowing a nonlocal return
00858  *  to the end of the <code>callcc</code> block from anywhere within a
00859  *  program. Continuations are somewhat analogous to a structured
00860  *  version of C's <code>setjmp/longjmp</code> (although they contain
00861  *  more state, so you might consider them closer to threads).
00862  *
00863  *  For instance:
00864  *
00865  *     require "continuation"
00866  *     arr = [ "Freddie", "Herbie", "Ron", "Max", "Ringo" ]
00867  *     callcc{|cc| $cc = cc}
00868  *     puts(message = arr.shift)
00869  *     $cc.call unless message =~ /Max/
00870  *
00871  *  <em>produces:</em>
00872  *
00873  *     Freddie
00874  *     Herbie
00875  *     Ron
00876  *     Max
00877  *
00878  *  This (somewhat contrived) example allows the inner loop to abandon
00879  *  processing early:
00880  *
00881  *     require "continuation"
00882  *     callcc {|cont|
00883  *       for i in 0..4
00884  *         print "\n#{i}: "
00885  *         for j in i*5...(i+1)*5
00886  *           cont.call() if j == 17
00887  *           printf "%3d", j
00888  *         end
00889  *       end
00890  *     }
00891  *     puts
00892  *
00893  *  <em>produces:</em>
00894  *
00895  *     0:   0  1  2  3  4
00896  *     1:   5  6  7  8  9
00897  *     2:  10 11 12 13 14
00898  *     3:  15 16
00899  */
00900 
00901 /*
00902  *  call-seq:
00903  *     callcc {|cont| block }   ->  obj
00904  *
00905  *  Generates a Continuation object, which it passes to
00906  *  the associated block. You need to <code>require
00907  *  'continuation'</code> before using this method. Performing a
00908  *  <em>cont</em><code>.call</code> will cause the #callcc
00909  *  to return (as will falling through the end of the block). The
00910  *  value returned by the #callcc is the value of the
00911  *  block, or the value passed to <em>cont</em><code>.call</code>. See
00912  *  class Continuation for more details. Also see
00913  *  Kernel#throw for an alternative mechanism for
00914  *  unwinding a call stack.
00915  */
00916 
00917 static VALUE
00918 rb_callcc(VALUE self)
00919 {
00920     volatile int called;
00921     volatile VALUE val = cont_capture(&called);
00922 
00923     if (called) {
00924         return val;
00925     }
00926     else {
00927         return rb_yield(val);
00928     }
00929 }
00930 
00931 static VALUE
00932 make_passing_arg(int argc, VALUE *argv)
00933 {
00934     switch (argc) {
00935       case 0:
00936         return Qnil;
00937       case 1:
00938         return argv[0];
00939       default:
00940         return rb_ary_new4(argc, argv);
00941     }
00942 }
00943 
00944 /* CAUTION!! : Currently, error in rollback_func is not supported  */
00945 /* same as rb_protect if set rollback_func to NULL */
00946 void
00947 ruby_register_rollback_func_for_ensure(VALUE (*ensure_func)(ANYARGS), VALUE (*rollback_func)(ANYARGS))
00948 {
00949     st_table **table_p = &GET_VM()->ensure_rollback_table;
00950     if (UNLIKELY(*table_p == NULL)) {
00951         *table_p = st_init_numtable();
00952     }
00953     st_insert(*table_p, (st_data_t)ensure_func, (st_data_t)rollback_func);
00954 }
00955 
00956 static inline VALUE
00957 lookup_rollback_func(VALUE (*ensure_func)(ANYARGS))
00958 {
00959     st_table *table = GET_VM()->ensure_rollback_table;
00960     st_data_t val;
00961     if (table && st_lookup(table, (st_data_t)ensure_func, &val))
00962         return (VALUE) val;
00963     return Qundef;
00964 }
00965 
00966 
00967 static inline void
00968 rollback_ensure_stack(VALUE self,rb_ensure_list_t *current,rb_ensure_entry_t *target)
00969 {
00970     rb_ensure_list_t *p;
00971     rb_ensure_entry_t *entry;
00972     size_t i;
00973     size_t cur_size;
00974     size_t target_size;
00975     size_t base_point;
00976     VALUE (*func)(ANYARGS);
00977 
00978     cur_size = 0;
00979     for (p=current; p; p=p->next)
00980         cur_size++;
00981     target_size = 0;
00982     for (entry=target; entry->marker; entry++)
00983         target_size++;
00984 
00985     /* search common stack point */
00986     p = current;
00987     base_point = cur_size;
00988     while (base_point) {
00989         if (target_size >= base_point &&
00990             p->entry.marker == target[target_size - base_point].marker)
00991             break;
00992         base_point --;
00993         p = p->next;
00994     }
00995 
00996     /* rollback function check */
00997     for (i=0; i < target_size - base_point; i++) {
00998         if (!lookup_rollback_func(target[i].e_proc)) {
00999             rb_raise(rb_eRuntimeError, "continuation called from out of critical rb_ensure scope");
01000         }
01001     }
01002     /* pop ensure stack */
01003     while (cur_size > base_point) {
01004         /* escape from ensure block */
01005         (*current->entry.e_proc)(current->entry.data2);
01006         current = current->next;
01007         cur_size--;
01008     }
01009     /* push ensure stack */
01010     while (i--) {
01011         func = (VALUE (*)(ANYARGS)) lookup_rollback_func(target[i].e_proc);
01012         if ((VALUE)func != Qundef) {
01013             (*func)(target[i].data2);
01014         }
01015     }
01016 }
01017 
01018 /*
01019  *  call-seq:
01020  *     cont.call(args, ...)
01021  *     cont[args, ...]
01022  *
01023  *  Invokes the continuation. The program continues from the end of the
01024  *  <code>callcc</code> block. If no arguments are given, the original
01025  *  <code>callcc</code> returns <code>nil</code>. If one argument is
01026  *  given, <code>callcc</code> returns it. Otherwise, an array
01027  *  containing <i>args</i> is returned.
01028  *
01029  *     callcc {|cont|  cont.call }           #=> nil
01030  *     callcc {|cont|  cont.call 1 }         #=> 1
01031  *     callcc {|cont|  cont.call 1, 2, 3 }   #=> [1, 2, 3]
01032  */
01033 
01034 static VALUE
01035 rb_cont_call(int argc, VALUE *argv, VALUE contval)
01036 {
01037     rb_context_t *cont;
01038     rb_thread_t *th = GET_THREAD();
01039     GetContPtr(contval, cont);
01040 
01041     if (cont->saved_thread.self != th->self) {
01042         rb_raise(rb_eRuntimeError, "continuation called across threads");
01043     }
01044     if (cont->saved_thread.protect_tag != th->protect_tag) {
01045         rb_raise(rb_eRuntimeError, "continuation called across stack rewinding barrier");
01046     }
01047     if (cont->saved_thread.fiber) {
01048         rb_fiber_t *fcont;
01049         GetFiberPtr(cont->saved_thread.fiber, fcont);
01050 
01051         if (th->fiber != cont->saved_thread.fiber) {
01052             rb_raise(rb_eRuntimeError, "continuation called across fiber");
01053         }
01054     }
01055     rollback_ensure_stack(contval, th->ensure_list, cont->ensure_array);
01056 
01057     cont->argc = argc;
01058     cont->value = make_passing_arg(argc, argv);
01059 
01060     /* restore `tracing' context. see [Feature #4347] */
01061     th->trace_arg = cont->saved_thread.trace_arg;
01062 
01063     cont_restore_0(cont, &contval);
01064     return Qnil; /* unreachable */
01065 }
01066 
01067 /*********/
01068 /* fiber */
01069 /*********/
01070 
01071 /*
01072  *  Document-class: Fiber
01073  *
01074  *  Fibers are primitives for implementing light weight cooperative
01075  *  concurrency in Ruby. Basically they are a means of creating code blocks
01076  *  that can be paused and resumed, much like threads. The main difference
01077  *  is that they are never preempted and that the scheduling must be done by
01078  *  the programmer and not the VM.
01079  *
01080  *  As opposed to other stackless light weight concurrency models, each fiber
01081  *  comes with a small 4KB stack. This enables the fiber to be paused from deeply
01082  *  nested function calls within the fiber block.
01083  *
01084  *  When a fiber is created it will not run automatically. Rather it must be
01085  *  be explicitly asked to run using the <code>Fiber#resume</code> method.
01086  *  The code running inside the fiber can give up control by calling
01087  *  <code>Fiber.yield</code> in which case it yields control back to caller
01088  *  (the caller of the <code>Fiber#resume</code>).
01089  *
01090  *  Upon yielding or termination the Fiber returns the value of the last
01091  *  executed expression
01092  *
01093  *  For instance:
01094  *
01095  *    fiber = Fiber.new do
01096  *      Fiber.yield 1
01097  *      2
01098  *    end
01099  *
01100  *    puts fiber.resume
01101  *    puts fiber.resume
01102  *    puts fiber.resume
01103  *
01104  *  <em>produces</em>
01105  *
01106  *    1
01107  *    2
01108  *    FiberError: dead fiber called
01109  *
01110  *  The <code>Fiber#resume</code> method accepts an arbitrary number of
01111  *  parameters, if it is the first call to <code>resume</code> then they
01112  *  will be passed as block arguments. Otherwise they will be the return
01113  *  value of the call to <code>Fiber.yield</code>
01114  *
01115  *  Example:
01116  *
01117  *    fiber = Fiber.new do |first|
01118  *      second = Fiber.yield first + 2
01119  *    end
01120  *
01121  *    puts fiber.resume 10
01122  *    puts fiber.resume 14
01123  *    puts fiber.resume 18
01124  *
01125  *  <em>produces</em>
01126  *
01127  *    12
01128  *    14
01129  *    FiberError: dead fiber called
01130  *
01131  */
01132 
01133 static const rb_data_type_t fiber_data_type = {
01134     "fiber",
01135     {fiber_mark, fiber_free, fiber_memsize,},
01136     NULL, NULL, RUBY_TYPED_FREE_IMMEDIATELY
01137 };
01138 
01139 static VALUE
01140 fiber_alloc(VALUE klass)
01141 {
01142     return TypedData_Wrap_Struct(klass, &fiber_data_type, 0);
01143 }
01144 
01145 static rb_fiber_t*
01146 fiber_t_alloc(VALUE fibval)
01147 {
01148     rb_fiber_t *fib;
01149     rb_thread_t *th = GET_THREAD();
01150 
01151     if (DATA_PTR(fibval) != 0) {
01152         rb_raise(rb_eRuntimeError, "cannot initialize twice");
01153     }
01154 
01155     THREAD_MUST_BE_RUNNING(th);
01156     fib = ALLOC(rb_fiber_t);
01157     memset(fib, 0, sizeof(rb_fiber_t));
01158     fib->cont.self = fibval;
01159     fib->cont.type = FIBER_CONTEXT;
01160     cont_init(&fib->cont, th);
01161     fib->prev = Qnil;
01162     fib->status = CREATED;
01163 
01164     DATA_PTR(fibval) = fib;
01165 
01166     return fib;
01167 }
01168 
01169 static VALUE
01170 fiber_init(VALUE fibval, VALUE proc)
01171 {
01172     rb_fiber_t *fib = fiber_t_alloc(fibval);
01173     rb_context_t *cont = &fib->cont;
01174     rb_thread_t *th = &cont->saved_thread;
01175 
01176     /* initialize cont */
01177     cont->vm_stack = 0;
01178 
01179     th->stack = 0;
01180     th->stack_size = 0;
01181 
01182     fiber_link_join(fib);
01183 
01184     th->stack_size = th->vm->default_params.fiber_vm_stack_size / sizeof(VALUE);
01185     th->stack = ALLOC_N(VALUE, th->stack_size);
01186 
01187     th->cfp = (void *)(th->stack + th->stack_size);
01188     th->cfp--;
01189     th->cfp->pc = 0;
01190     th->cfp->sp = th->stack + 1;
01191 #if VM_DEBUG_BP_CHECK
01192     th->cfp->bp_check = 0;
01193 #endif
01194     th->cfp->ep = th->stack;
01195     *th->cfp->ep = VM_ENVVAL_BLOCK_PTR(0);
01196     th->cfp->self = Qnil;
01197     th->cfp->klass = Qnil;
01198     th->cfp->flag = 0;
01199     th->cfp->iseq = 0;
01200     th->cfp->proc = 0;
01201     th->cfp->block_iseq = 0;
01202     th->cfp->me = 0;
01203     th->tag = 0;
01204     th->local_storage = st_init_numtable();
01205 
01206     th->first_proc = proc;
01207 
01208 #if !FIBER_USE_NATIVE
01209     MEMCPY(&cont->jmpbuf, &th->root_jmpbuf, rb_jmpbuf_t, 1);
01210 #endif
01211 
01212     return fibval;
01213 }
01214 
01215 /* :nodoc: */
01216 static VALUE
01217 rb_fiber_init(VALUE fibval)
01218 {
01219     return fiber_init(fibval, rb_block_proc());
01220 }
01221 
01222 VALUE
01223 rb_fiber_new(VALUE (*func)(ANYARGS), VALUE obj)
01224 {
01225     return fiber_init(fiber_alloc(rb_cFiber), rb_proc_new(func, obj));
01226 }
01227 
01228 static VALUE
01229 return_fiber(void)
01230 {
01231     rb_fiber_t *fib;
01232     VALUE curr = rb_fiber_current();
01233     VALUE prev;
01234     GetFiberPtr(curr, fib);
01235 
01236     prev = fib->prev;
01237     if (NIL_P(prev)) {
01238         const VALUE root_fiber = GET_THREAD()->root_fiber;
01239 
01240         if (root_fiber == curr) {
01241             rb_raise(rb_eFiberError, "can't yield from root fiber");
01242         }
01243         return root_fiber;
01244     }
01245     else {
01246         fib->prev = Qnil;
01247         return prev;
01248     }
01249 }
01250 
01251 VALUE rb_fiber_transfer(VALUE fib, int argc, VALUE *argv);
01252 
01253 static void
01254 rb_fiber_terminate(rb_fiber_t *fib)
01255 {
01256     VALUE value = fib->cont.value;
01257     fib->status = TERMINATED;
01258 #if FIBER_USE_NATIVE && !defined(_WIN32)
01259     /* Ruby must not switch to other thread until storing terminated_machine_stack */
01260     terminated_machine_stack.ptr = fib->context.uc_stack.ss_sp;
01261     terminated_machine_stack.size = fib->context.uc_stack.ss_size / sizeof(VALUE);
01262     fib->context.uc_stack.ss_sp = NULL;
01263     fib->cont.machine.stack = NULL;
01264     fib->cont.machine.stack_size = 0;
01265 #endif
01266     rb_fiber_transfer(return_fiber(), 1, &value);
01267 }
01268 
01269 void
01270 rb_fiber_start(void)
01271 {
01272     rb_thread_t *th = GET_THREAD();
01273     rb_fiber_t *fib;
01274     rb_context_t *cont;
01275     rb_proc_t *proc;
01276     int state;
01277 
01278     GetFiberPtr(th->fiber, fib);
01279     cont = &fib->cont;
01280 
01281     TH_PUSH_TAG(th);
01282     if ((state = EXEC_TAG()) == 0) {
01283         int argc;
01284         const VALUE *argv, args = cont->value;
01285         GetProcPtr(cont->saved_thread.first_proc, proc);
01286         argv = (argc = cont->argc) > 1 ? RARRAY_CONST_PTR(args) : &args;
01287         cont->value = Qnil;
01288         th->errinfo = Qnil;
01289         th->root_lep = rb_vm_ep_local_ep(proc->block.ep);
01290         th->root_svar = Qnil;
01291 
01292         fib->status = RUNNING;
01293         cont->value = rb_vm_invoke_proc(th, proc, argc, argv, 0);
01294     }
01295     TH_POP_TAG();
01296 
01297     if (state) {
01298         if (state == TAG_RAISE || state == TAG_FATAL) {
01299             rb_threadptr_pending_interrupt_enque(th, th->errinfo);
01300         }
01301         else {
01302             VALUE err = rb_vm_make_jump_tag_but_local_jump(state, th->errinfo);
01303             if (!NIL_P(err))
01304                 rb_threadptr_pending_interrupt_enque(th, err);
01305         }
01306         RUBY_VM_SET_INTERRUPT(th);
01307     }
01308 
01309     rb_fiber_terminate(fib);
01310     rb_bug("rb_fiber_start: unreachable");
01311 }
01312 
01313 static rb_fiber_t *
01314 root_fiber_alloc(rb_thread_t *th)
01315 {
01316     rb_fiber_t *fib;
01317     /* no need to allocate vm stack */
01318     fib = fiber_t_alloc(fiber_alloc(rb_cFiber));
01319     fib->cont.type = ROOT_FIBER_CONTEXT;
01320 #if FIBER_USE_NATIVE
01321 #ifdef _WIN32
01322     fib->fib_handle = ConvertThreadToFiber(0);
01323 #endif
01324 #endif
01325     fib->status = RUNNING;
01326     fib->prev_fiber = fib->next_fiber = fib;
01327 
01328     return fib;
01329 }
01330 
01331 VALUE
01332 rb_fiber_current(void)
01333 {
01334     rb_thread_t *th = GET_THREAD();
01335     if (th->fiber == 0) {
01336         /* save root */
01337         rb_fiber_t *fib = root_fiber_alloc(th);
01338         th->root_fiber = th->fiber = fib->cont.self;
01339     }
01340     return th->fiber;
01341 }
01342 
01343 static VALUE
01344 fiber_store(rb_fiber_t *next_fib)
01345 {
01346     rb_thread_t *th = GET_THREAD();
01347     rb_fiber_t *fib;
01348 
01349     if (th->fiber) {
01350         GetFiberPtr(th->fiber, fib);
01351         cont_save_thread(&fib->cont, th);
01352     }
01353     else {
01354         /* create current fiber */
01355         fib = root_fiber_alloc(th);
01356         th->root_fiber = th->fiber = fib->cont.self;
01357     }
01358 
01359 #if !FIBER_USE_NATIVE
01360     cont_save_machine_stack(th, &fib->cont);
01361 #endif
01362 
01363     if (FIBER_USE_NATIVE || ruby_setjmp(fib->cont.jmpbuf)) {
01364 #if FIBER_USE_NATIVE
01365         fiber_setcontext(next_fib, fib);
01366 #ifndef _WIN32
01367         if (terminated_machine_stack.ptr) {
01368             if (machine_stack_cache_index < MAX_MACHINE_STACK_CACHE) {
01369                 machine_stack_cache[machine_stack_cache_index].ptr = terminated_machine_stack.ptr;
01370                 machine_stack_cache[machine_stack_cache_index].size = terminated_machine_stack.size;
01371                 machine_stack_cache_index++;
01372             }
01373             else {
01374                 if (terminated_machine_stack.ptr != fib->cont.machine.stack) {
01375                     munmap((void*)terminated_machine_stack.ptr, terminated_machine_stack.size * sizeof(VALUE));
01376                 }
01377                 else {
01378                     rb_bug("terminated fiber resumed");
01379                 }
01380             }
01381             terminated_machine_stack.ptr = NULL;
01382             terminated_machine_stack.size = 0;
01383         }
01384 #endif
01385 #endif
01386         /* restored */
01387         GetFiberPtr(th->fiber, fib);
01388         if (fib->cont.argc == -1) rb_exc_raise(fib->cont.value);
01389         return fib->cont.value;
01390     }
01391 #if !FIBER_USE_NATIVE
01392     else {
01393         return Qundef;
01394     }
01395 #endif
01396 }
01397 
01398 static inline VALUE
01399 fiber_switch(VALUE fibval, int argc, VALUE *argv, int is_resume)
01400 {
01401     VALUE value;
01402     rb_fiber_t *fib;
01403     rb_context_t *cont;
01404     rb_thread_t *th = GET_THREAD();
01405 
01406     GetFiberPtr(fibval, fib);
01407     cont = &fib->cont;
01408 
01409     if (th->fiber == fibval) {
01410         /* ignore fiber context switch
01411          * because destination fiber is same as current fiber
01412          */
01413         return make_passing_arg(argc, argv);
01414     }
01415 
01416     if (cont->saved_thread.self != th->self) {
01417         rb_raise(rb_eFiberError, "fiber called across threads");
01418     }
01419     else if (cont->saved_thread.protect_tag != th->protect_tag) {
01420         rb_raise(rb_eFiberError, "fiber called across stack rewinding barrier");
01421     }
01422     else if (fib->status == TERMINATED) {
01423         value = rb_exc_new2(rb_eFiberError, "dead fiber called");
01424         if (th->fiber != fibval) {
01425             GetFiberPtr(th->fiber, fib);
01426             if (fib->status != TERMINATED) rb_exc_raise(value);
01427             fibval = th->root_fiber;
01428         }
01429         else {
01430             fibval = fib->prev;
01431             if (NIL_P(fibval)) fibval = th->root_fiber;
01432         }
01433         GetFiberPtr(fibval, fib);
01434         cont = &fib->cont;
01435         cont->argc = -1;
01436         cont->value = value;
01437 #if FIBER_USE_NATIVE
01438         {
01439             VALUE oldfibval;
01440             rb_fiber_t *oldfib;
01441             oldfibval = rb_fiber_current();
01442             GetFiberPtr(oldfibval, oldfib);
01443             fiber_setcontext(fib, oldfib);
01444         }
01445 #else
01446         cont_restore_0(cont, &value);
01447 #endif
01448     }
01449 
01450     if (is_resume) {
01451         fib->prev = rb_fiber_current();
01452     }
01453     else {
01454         /* restore `tracing' context. see [Feature #4347] */
01455         th->trace_arg = cont->saved_thread.trace_arg;
01456     }
01457 
01458     cont->argc = argc;
01459     cont->value = make_passing_arg(argc, argv);
01460 
01461     value = fiber_store(fib);
01462 #if !FIBER_USE_NATIVE
01463     if (value == Qundef) {
01464         cont_restore_0(cont, &value);
01465         rb_bug("rb_fiber_resume: unreachable");
01466     }
01467 #endif
01468     RUBY_VM_CHECK_INTS(th);
01469 
01470     return value;
01471 }
01472 
01473 VALUE
01474 rb_fiber_transfer(VALUE fib, int argc, VALUE *argv)
01475 {
01476     return fiber_switch(fib, argc, argv, 0);
01477 }
01478 
01479 VALUE
01480 rb_fiber_resume(VALUE fibval, int argc, VALUE *argv)
01481 {
01482     rb_fiber_t *fib;
01483     GetFiberPtr(fibval, fib);
01484 
01485     if (fib->prev != Qnil || fib->cont.type == ROOT_FIBER_CONTEXT) {
01486         rb_raise(rb_eFiberError, "double resume");
01487     }
01488     if (fib->transfered != 0) {
01489         rb_raise(rb_eFiberError, "cannot resume transferred Fiber");
01490     }
01491 
01492     return fiber_switch(fibval, argc, argv, 1);
01493 }
01494 
01495 VALUE
01496 rb_fiber_yield(int argc, VALUE *argv)
01497 {
01498     return rb_fiber_transfer(return_fiber(), argc, argv);
01499 }
01500 
01501 void
01502 rb_fiber_reset_root_local_storage(VALUE thval)
01503 {
01504     rb_thread_t *th;
01505     rb_fiber_t  *fib;
01506 
01507     GetThreadPtr(thval, th);
01508     if (th->root_fiber && th->root_fiber != th->fiber) {
01509         GetFiberPtr(th->root_fiber, fib);
01510         th->local_storage = fib->cont.saved_thread.local_storage;
01511     }
01512 }
01513 
01514 /*
01515  *  call-seq:
01516  *     fiber.alive? -> true or false
01517  *
01518  *  Returns true if the fiber can still be resumed (or transferred
01519  *  to). After finishing execution of the fiber block this method will
01520  *  always return false. You need to <code>require 'fiber'</code>
01521  *  before using this method.
01522  */
01523 VALUE
01524 rb_fiber_alive_p(VALUE fibval)
01525 {
01526     rb_fiber_t *fib;
01527     GetFiberPtr(fibval, fib);
01528     return fib->status != TERMINATED ? Qtrue : Qfalse;
01529 }
01530 
01531 /*
01532  *  call-seq:
01533  *     fiber.resume(args, ...) -> obj
01534  *
01535  *  Resumes the fiber from the point at which the last <code>Fiber.yield</code>
01536  *  was called, or starts running it if it is the first call to
01537  *  <code>resume</code>. Arguments passed to resume will be the value of
01538  *  the <code>Fiber.yield</code> expression or will be passed as block
01539  *  parameters to the fiber's block if this is the first <code>resume</code>.
01540  *
01541  *  Alternatively, when resume is called it evaluates to the arguments passed
01542  *  to the next <code>Fiber.yield</code> statement inside the fiber's block
01543  *  or to the block value if it runs to completion without any
01544  *  <code>Fiber.yield</code>
01545  */
01546 static VALUE
01547 rb_fiber_m_resume(int argc, VALUE *argv, VALUE fib)
01548 {
01549     return rb_fiber_resume(fib, argc, argv);
01550 }
01551 
01552 /*
01553  *  call-seq:
01554  *     fiber.transfer(args, ...) -> obj
01555  *
01556  *  Transfer control to another fiber, resuming it from where it last
01557  *  stopped or starting it if it was not resumed before. The calling
01558  *  fiber will be suspended much like in a call to
01559  *  <code>Fiber.yield</code>. You need to <code>require 'fiber'</code>
01560  *  before using this method.
01561  *
01562  *  The fiber which receives the transfer call is treats it much like
01563  *  a resume call. Arguments passed to transfer are treated like those
01564  *  passed to resume.
01565  *
01566  *  You cannot resume a fiber that transferred control to another one.
01567  *  This will cause a double resume error. You need to transfer control
01568  *  back to this fiber before it can yield and resume.
01569  *
01570  *  Example:
01571  *
01572  *    fiber1 = Fiber.new do
01573  *      puts "In Fiber 1"
01574  *      Fiber.yield
01575  *    end
01576  *
01577  *    fiber2 = Fiber.new do
01578  *      puts "In Fiber 2"
01579  *      fiber1.transfer
01580  *      puts "Never see this message"
01581  *    end
01582  *
01583  *    fiber3 = Fiber.new do
01584  *      puts "In Fiber 3"
01585  *    end
01586  *
01587  *    fiber2.resume
01588  *    fiber3.resume
01589  *
01590  *    <em>produces</em>
01591  *
01592  *    In fiber 2
01593  *    In fiber 1
01594  *    In fiber 3
01595  *
01596  */
01597 static VALUE
01598 rb_fiber_m_transfer(int argc, VALUE *argv, VALUE fibval)
01599 {
01600     rb_fiber_t *fib;
01601     GetFiberPtr(fibval, fib);
01602     fib->transfered = 1;
01603     return rb_fiber_transfer(fibval, argc, argv);
01604 }
01605 
01606 /*
01607  *  call-seq:
01608  *     Fiber.yield(args, ...) -> obj
01609  *
01610  *  Yields control back to the context that resumed the fiber, passing
01611  *  along any arguments that were passed to it. The fiber will resume
01612  *  processing at this point when <code>resume</code> is called next.
01613  *  Any arguments passed to the next <code>resume</code> will be the
01614  *  value that this <code>Fiber.yield</code> expression evaluates to.
01615  */
01616 static VALUE
01617 rb_fiber_s_yield(int argc, VALUE *argv, VALUE klass)
01618 {
01619     return rb_fiber_yield(argc, argv);
01620 }
01621 
01622 /*
01623  *  call-seq:
01624  *     Fiber.current() -> fiber
01625  *
01626  *  Returns the current fiber. You need to <code>require 'fiber'</code>
01627  *  before using this method. If you are not running in the context of
01628  *  a fiber this method will return the root fiber.
01629  */
01630 static VALUE
01631 rb_fiber_s_current(VALUE klass)
01632 {
01633     return rb_fiber_current();
01634 }
01635 
01636 
01637 
01638 /*
01639  *  Document-class: FiberError
01640  *
01641  *  Raised when an invalid operation is attempted on a Fiber, in
01642  *  particular when attempting to call/resume a dead fiber,
01643  *  attempting to yield from the root fiber, or calling a fiber across
01644  *  threads.
01645  *
01646  *     fiber = Fiber.new{}
01647  *     fiber.resume #=> nil
01648  *     fiber.resume #=> FiberError: dead fiber called
01649  */
01650 
01651 void
01652 Init_Cont(void)
01653 {
01654 #if FIBER_USE_NATIVE
01655     rb_thread_t *th = GET_THREAD();
01656 
01657 #ifdef _WIN32
01658     SYSTEM_INFO info;
01659     GetSystemInfo(&info);
01660     pagesize = info.dwPageSize;
01661 #else /* not WIN32 */
01662     pagesize = sysconf(_SC_PAGESIZE);
01663 #endif
01664     SET_MACHINE_STACK_END(&th->machine.stack_end);
01665 #endif
01666 
01667     rb_cFiber = rb_define_class("Fiber", rb_cObject);
01668     rb_define_alloc_func(rb_cFiber, fiber_alloc);
01669     rb_eFiberError = rb_define_class("FiberError", rb_eStandardError);
01670     rb_define_singleton_method(rb_cFiber, "yield", rb_fiber_s_yield, -1);
01671     rb_define_method(rb_cFiber, "initialize", rb_fiber_init, 0);
01672     rb_define_method(rb_cFiber, "resume", rb_fiber_m_resume, -1);
01673 }
01674 
01675 RUBY_SYMBOL_EXPORT_BEGIN
01676 
01677 void
01678 ruby_Init_Continuation_body(void)
01679 {
01680     rb_cContinuation = rb_define_class("Continuation", rb_cObject);
01681     rb_undef_alloc_func(rb_cContinuation);
01682     rb_undef_method(CLASS_OF(rb_cContinuation), "new");
01683     rb_define_method(rb_cContinuation, "call", rb_cont_call, -1);
01684     rb_define_method(rb_cContinuation, "[]", rb_cont_call, -1);
01685     rb_define_global_function("callcc", rb_callcc, 0);
01686 }
01687 
01688 void
01689 ruby_Init_Fiber_as_Coroutine(void)
01690 {
01691     rb_define_method(rb_cFiber, "transfer", rb_fiber_m_transfer, -1);
01692     rb_define_method(rb_cFiber, "alive?", rb_fiber_alive_p, 0);
01693     rb_define_singleton_method(rb_cFiber, "current", rb_fiber_s_current, 0);
01694 }
01695 
01696 RUBY_SYMBOL_EXPORT_END
01697 

Generated on 19 Jul 2016 for Ruby by  doxygen 1.4.7