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