Ruby  2.1.3p242(2014-09-19revision47630)
cont.c
Go to the documentation of this file.
1 /**********************************************************************
2 
3  cont.c -
4 
5  $Author: nagachika $
6  created at: Thu May 23 09:03:43 2007
7 
8  Copyright (C) 2007 Koichi Sasada
9 
10 **********************************************************************/
11 
12 #include "ruby/ruby.h"
13 #include "internal.h"
14 #include "vm_core.h"
15 #include "gc.h"
16 #include "eval_intern.h"
17 
18 /* FIBER_USE_NATIVE enables Fiber performance improvement using system
19  * dependent method such as make/setcontext on POSIX system or
20  * CreateFiber() API on Windows.
21  * This hack make Fiber context switch faster (x2 or more).
22  * However, it decrease maximum number of Fiber. For example, on the
23  * 32bit POSIX OS, ten or twenty thousands Fiber can be created.
24  *
25  * Details is reported in the paper "A Fast Fiber Implementation for Ruby 1.9"
26  * in Proc. of 51th Programming Symposium, pp.21--28 (2010) (in Japanese).
27  */
28 
29 #if !defined(FIBER_USE_NATIVE)
30 # if defined(HAVE_GETCONTEXT) && defined(HAVE_SETCONTEXT)
31 # if 0
32 # elif defined(__NetBSD__)
33 /* On our experience, NetBSD doesn't support using setcontext() and pthread
34  * simultaneously. This is because pthread_self(), TLS and other information
35  * are represented by stack pointer (higher bits of stack pointer).
36  * TODO: check such constraint on configure.
37  */
38 # define FIBER_USE_NATIVE 0
39 # elif defined(__sun)
40 /* On Solaris because resuming any Fiber caused SEGV, for some reason.
41  */
42 # define FIBER_USE_NATIVE 0
43 # elif defined(__ia64)
44 /* At least, Linux/ia64's getcontext(3) doesn't save register window.
45  */
46 # define FIBER_USE_NATIVE 0
47 # elif defined(__GNU__)
48 /* GNU/Hurd doesn't fully support getcontext, setcontext, makecontext
49  * and swapcontext functions. Disabling their usage till support is
50  * implemented. More info at
51  * http://darnassus.sceen.net/~hurd-web/open_issues/glibc/#getcontext
52  */
53 # define FIBER_USE_NATIVE 0
54 # else
55 # define FIBER_USE_NATIVE 1
56 # endif
57 # elif defined(_WIN32)
58 # if _WIN32_WINNT >= 0x0400
59 /* only when _WIN32_WINNT >= 0x0400 on Windows because Fiber APIs are
60  * supported only such building (and running) environments.
61  * [ruby-dev:41192]
62  */
63 # define FIBER_USE_NATIVE 1
64 # endif
65 # endif
66 #endif
67 #if !defined(FIBER_USE_NATIVE)
68 #define FIBER_USE_NATIVE 0
69 #endif
70 
71 #if FIBER_USE_NATIVE
72 #ifndef _WIN32
73 #include <unistd.h>
74 #include <sys/mman.h>
75 #include <ucontext.h>
76 #endif
77 #define RB_PAGE_SIZE (pagesize)
78 #define RB_PAGE_MASK (~(RB_PAGE_SIZE - 1))
79 static long pagesize;
80 #endif /*FIBER_USE_NATIVE*/
81 
82 #define CAPTURE_JUST_VALID_VM_STACK 1
83 
88 };
89 
90 typedef struct rb_context_struct {
92  VALUE self;
93  int argc;
96 #ifdef CAPTURE_JUST_VALID_VM_STACK
97  size_t vm_stack_slen; /* length of stack (head of th->stack) */
98  size_t vm_stack_clen; /* length of control frames (tail of th->stack) */
99 #endif
100  struct {
103  size_t stack_size;
104 #ifdef __ia64
105  VALUE *register_stack;
106  VALUE *register_stack_src;
107  int register_stack_size;
108 #endif
109  } machine;
114 } rb_context_t;
115 
120 };
121 
122 #if FIBER_USE_NATIVE && !defined(_WIN32)
123 #define MAX_MACHINE_STACK_CACHE 10
124 static int machine_stack_cache_index = 0;
125 typedef struct machine_stack_cache_struct {
126  void *ptr;
127  size_t size;
128 } machine_stack_cache_t;
129 static machine_stack_cache_t machine_stack_cache[MAX_MACHINE_STACK_CACHE];
130 static machine_stack_cache_t terminated_machine_stack;
131 #endif
132 
133 typedef struct rb_fiber_struct {
139  /* If a fiber invokes "transfer",
140  * then this fiber can't "resume" any more after that.
141  * You shouldn't mix "transfer" and "resume".
142  */
144 
145 #if FIBER_USE_NATIVE
146 #ifdef _WIN32
147  void *fib_handle;
148 #else
149  ucontext_t context;
150 #endif
151 #endif
152 } rb_fiber_t;
153 
158 
159 #define GetContPtr(obj, ptr) \
160  TypedData_Get_Struct((obj), rb_context_t, &cont_data_type, (ptr))
161 
162 #define GetFiberPtr(obj, ptr) do {\
163  TypedData_Get_Struct((obj), rb_fiber_t, &fiber_data_type, (ptr)); \
164  if (!(ptr)) rb_raise(rb_eFiberError, "uninitialized fiber"); \
165 } while (0)
166 
167 NOINLINE(static VALUE cont_capture(volatile int *stat));
168 
169 #define THREAD_MUST_BE_RUNNING(th) do { \
170  if (!(th)->tag) rb_raise(rb_eThreadError, "not running thread"); \
171  } while (0)
172 
173 static void
174 cont_mark(void *ptr)
175 {
176  RUBY_MARK_ENTER("cont");
177  if (ptr) {
178  rb_context_t *cont = ptr;
179  rb_gc_mark(cont->value);
182 
183  if (cont->vm_stack) {
184 #ifdef CAPTURE_JUST_VALID_VM_STACK
186  cont->vm_stack + cont->vm_stack_slen + cont->vm_stack_clen);
187 #else
188  rb_gc_mark_localtion(cont->vm_stack,
189  cont->vm_stack, cont->saved_thread.stack_size);
190 #endif
191  }
192 
193  if (cont->machine.stack) {
194  if (cont->type == CONTINUATION_CONTEXT) {
195  /* cont */
197  cont->machine.stack + cont->machine.stack_size);
198  }
199  else {
200  /* fiber */
201  rb_thread_t *th;
202  rb_fiber_t *fib = (rb_fiber_t*)cont;
203  GetThreadPtr(cont->saved_thread.self, th);
204  if ((th->fiber != cont->self) && fib->status == RUNNING) {
206  cont->machine.stack + cont->machine.stack_size);
207  }
208  }
209  }
210 #ifdef __ia64
211  if (cont->machine.register_stack) {
212  rb_gc_mark_locations(cont->machine.register_stack,
213  cont->machine.register_stack + cont->machine.register_stack_size);
214  }
215 #endif
216  }
217  RUBY_MARK_LEAVE("cont");
218 }
219 
220 static void
221 cont_free(void *ptr)
222 {
223  RUBY_FREE_ENTER("cont");
224  if (ptr) {
225  rb_context_t *cont = ptr;
226  RUBY_FREE_UNLESS_NULL(cont->saved_thread.stack); fflush(stdout);
227 #if FIBER_USE_NATIVE
228  if (cont->type == CONTINUATION_CONTEXT) {
229  /* cont */
230  ruby_xfree(cont->ensure_array);
232  }
233  else {
234  /* fiber */
235 #ifdef _WIN32
236  if (GET_THREAD()->fiber != cont->self && cont->type != ROOT_FIBER_CONTEXT) {
237  /* don't delete root fiber handle */
238  rb_fiber_t *fib = (rb_fiber_t*)cont;
239  if (fib->fib_handle) {
240  DeleteFiber(fib->fib_handle);
241  }
242  }
243 #else /* not WIN32 */
244  if (GET_THREAD()->fiber != cont->self) {
245  rb_fiber_t *fib = (rb_fiber_t*)cont;
246  if (fib->context.uc_stack.ss_sp) {
247  if (cont->type == ROOT_FIBER_CONTEXT) {
248  rb_bug("Illegal root fiber parameter");
249  }
250  munmap((void*)fib->context.uc_stack.ss_sp, fib->context.uc_stack.ss_size);
251  }
252  }
253  else {
254  /* It may reached here when finalize */
255  /* TODO examine whether it is a bug */
256  /* rb_bug("cont_free: release self"); */
257  }
258 #endif
259  }
260 #else /* not FIBER_USE_NATIVE */
261  ruby_xfree(cont->ensure_array);
263 #endif
264 #ifdef __ia64
265  RUBY_FREE_UNLESS_NULL(cont->machine.register_stack);
266 #endif
268 
269  /* free rb_cont_t or rb_fiber_t */
270  ruby_xfree(ptr);
271  }
272  RUBY_FREE_LEAVE("cont");
273 }
274 
275 static size_t
276 cont_memsize(const void *ptr)
277 {
278  const rb_context_t *cont = ptr;
279  size_t size = 0;
280  if (cont) {
281  size = sizeof(*cont);
282  if (cont->vm_stack) {
283 #ifdef CAPTURE_JUST_VALID_VM_STACK
284  size_t n = (cont->vm_stack_slen + cont->vm_stack_clen);
285 #else
286  size_t n = cont->saved_thread.stack_size;
287 #endif
288  size += n * sizeof(*cont->vm_stack);
289  }
290 
291  if (cont->machine.stack) {
292  size += cont->machine.stack_size * sizeof(*cont->machine.stack);
293  }
294 #ifdef __ia64
295  if (cont->machine.register_stack) {
296  size += cont->machine.register_stack_size * sizeof(*cont->machine.register_stack);
297  }
298 #endif
299  }
300  return size;
301 }
302 
303 static void
304 fiber_mark(void *ptr)
305 {
306  RUBY_MARK_ENTER("cont");
307  if (ptr) {
308  rb_fiber_t *fib = ptr;
309  rb_gc_mark(fib->prev);
310  cont_mark(&fib->cont);
311  }
312  RUBY_MARK_LEAVE("cont");
313 }
314 
315 static void
317 {
318  VALUE current_fibval = rb_fiber_current();
319  rb_fiber_t *current_fib;
320  GetFiberPtr(current_fibval, current_fib);
321 
322  /* join fiber link */
323  fib->next_fiber = current_fib->next_fiber;
324  fib->prev_fiber = current_fib;
325  current_fib->next_fiber->prev_fiber = fib;
326  current_fib->next_fiber = fib;
327 }
328 
329 static void
331 {
332  fib->prev_fiber->next_fiber = fib->next_fiber;
333  fib->next_fiber->prev_fiber = fib->prev_fiber;
334 }
335 
336 static void
337 fiber_free(void *ptr)
338 {
339  RUBY_FREE_ENTER("fiber");
340  if (ptr) {
341  rb_fiber_t *fib = ptr;
342  if (fib->cont.type != ROOT_FIBER_CONTEXT &&
345  }
346  fiber_link_remove(fib);
347 
348  cont_free(&fib->cont);
349  }
350  RUBY_FREE_LEAVE("fiber");
351 }
352 
353 static size_t
354 fiber_memsize(const void *ptr)
355 {
356  const rb_fiber_t *fib = ptr;
357  size_t size = 0;
358  if (ptr) {
359  size = sizeof(*fib);
360  if (fib->cont.type != ROOT_FIBER_CONTEXT &&
363  }
364  size += cont_memsize(&fib->cont);
365  }
366  return size;
367 }
368 
369 VALUE
371 {
372  if (rb_typeddata_is_kind_of(obj, &fiber_data_type)) {
373  return Qtrue;
374  }
375  else {
376  return Qfalse;
377  }
378 }
379 
380 static void
382 {
383  size_t size;
384 
386 #ifdef __ia64
387  th->machine.register_stack_end = rb_ia64_bsp();
388 #endif
389 
390  if (th->machine.stack_start > th->machine.stack_end) {
391  size = cont->machine.stack_size = th->machine.stack_start - th->machine.stack_end;
392  cont->machine.stack_src = th->machine.stack_end;
393  }
394  else {
395  size = cont->machine.stack_size = th->machine.stack_end - th->machine.stack_start;
396  cont->machine.stack_src = th->machine.stack_start;
397  }
398 
399  if (cont->machine.stack) {
400  REALLOC_N(cont->machine.stack, VALUE, size);
401  }
402  else {
403  cont->machine.stack = ALLOC_N(VALUE, size);
404  }
405 
407  MEMCPY(cont->machine.stack, cont->machine.stack_src, VALUE, size);
408 
409 #ifdef __ia64
410  rb_ia64_flushrs();
411  size = cont->machine.register_stack_size = th->machine.register_stack_end - th->machine.register_stack_start;
412  cont->machine.register_stack_src = th->machine.register_stack_start;
413  if (cont->machine.register_stack) {
414  REALLOC_N(cont->machine.register_stack, VALUE, size);
415  }
416  else {
417  cont->machine.register_stack = ALLOC_N(VALUE, size);
418  }
419 
420  MEMCPY(cont->machine.register_stack, cont->machine.register_stack_src, VALUE, size);
421 #endif
422 }
423 
424 static const rb_data_type_t cont_data_type = {
425  "continuation",
428 };
429 
430 static void
432 {
433  /* save thread context */
434  cont->saved_thread = *th;
435  /* saved_thread->machine.stack_(start|end) should be NULL */
436  /* because it may happen GC afterward */
437  cont->saved_thread.machine.stack_start = 0;
438  cont->saved_thread.machine.stack_end = 0;
439 #ifdef __ia64
440  cont->saved_thread.machine.register_stack_start = 0;
441  cont->saved_thread.machine.register_stack_end = 0;
442 #endif
443 }
444 
445 static void
447 {
448  /* save thread context */
449  cont_save_thread(cont, th);
450  cont->saved_thread.local_storage = 0;
451 }
452 
453 static rb_context_t *
455 {
457  volatile VALUE contval;
458  rb_thread_t *th = GET_THREAD();
459 
461  contval = TypedData_Make_Struct(klass, rb_context_t, &cont_data_type, cont);
462  cont->self = contval;
463  cont_init(cont, th);
464  return cont;
465 }
466 
467 static VALUE
468 cont_capture(volatile int *stat)
469 {
471  rb_thread_t *th = GET_THREAD(), *sth;
472  volatile VALUE contval;
473 
476  cont = cont_new(rb_cContinuation);
477  contval = cont->self;
478  sth = &cont->saved_thread;
479 
480 #ifdef CAPTURE_JUST_VALID_VM_STACK
481  cont->vm_stack_slen = th->cfp->sp + th->mark_stack_len - th->stack;
482  cont->vm_stack_clen = th->stack + th->stack_size - (VALUE*)th->cfp;
483  cont->vm_stack = ALLOC_N(VALUE, cont->vm_stack_slen + cont->vm_stack_clen);
484  MEMCPY(cont->vm_stack, th->stack, VALUE, cont->vm_stack_slen);
485  MEMCPY(cont->vm_stack + cont->vm_stack_slen, (VALUE*)th->cfp, VALUE, cont->vm_stack_clen);
486 #else
487  cont->vm_stack = ALLOC_N(VALUE, th->stack_size);
488  MEMCPY(cont->vm_stack, th->stack, VALUE, th->stack_size);
489 #endif
490  sth->stack = 0;
491 
492  cont_save_machine_stack(th, cont);
493 
494  /* backup ensure_list to array for search in another context */
495  {
496  rb_ensure_list_t *p;
497  int size = 0;
498  rb_ensure_entry_t *entry;
499  for (p=th->ensure_list; p; p=p->next)
500  size++;
501  entry = cont->ensure_array = ALLOC_N(rb_ensure_entry_t,size+1);
502  for (p=th->ensure_list; p; p=p->next) {
503  if (!p->entry.marker)
504  p->entry.marker = rb_ary_tmp_new(0); /* dummy object */
505  *entry++ = p->entry;
506  }
507  entry->marker = 0;
508  }
509 
510  if (ruby_setjmp(cont->jmpbuf)) {
511  volatile VALUE value;
512 
513  value = cont->value;
514  if (cont->argc == -1) rb_exc_raise(value);
515  cont->value = Qnil;
516  *stat = 1;
517  return value;
518  }
519  else {
520  *stat = 0;
521  return contval;
522  }
523 }
524 
525 static void
527 {
528  rb_thread_t *th = GET_THREAD(), *sth = &cont->saved_thread;
529 
530  /* restore thread context */
531  if (cont->type == CONTINUATION_CONTEXT) {
532  /* continuation */
533  VALUE fib;
534 
535  th->fiber = sth->fiber;
536  fib = th->fiber ? th->fiber : th->root_fiber;
537 
538  if (fib) {
539  rb_fiber_t *fcont;
540  GetFiberPtr(fib, fcont);
541  th->stack_size = fcont->cont.saved_thread.stack_size;
542  th->stack = fcont->cont.saved_thread.stack;
543  }
544 #ifdef CAPTURE_JUST_VALID_VM_STACK
545  MEMCPY(th->stack, cont->vm_stack, VALUE, cont->vm_stack_slen);
546  MEMCPY(th->stack + sth->stack_size - cont->vm_stack_clen,
547  cont->vm_stack + cont->vm_stack_slen, VALUE, cont->vm_stack_clen);
548 #else
549  MEMCPY(th->stack, cont->vm_stack, VALUE, sth->stack_size);
550 #endif
551  }
552  else {
553  /* fiber */
554  th->stack = sth->stack;
555  th->stack_size = sth->stack_size;
556  th->local_storage = sth->local_storage;
557  th->fiber = cont->self;
558  }
559 
560  th->cfp = sth->cfp;
561  th->safe_level = sth->safe_level;
562  th->raised_flag = sth->raised_flag;
563  th->state = sth->state;
564  th->status = sth->status;
565  th->tag = sth->tag;
566  th->protect_tag = sth->protect_tag;
567  th->errinfo = sth->errinfo;
568  th->first_proc = sth->first_proc;
569  th->root_lep = sth->root_lep;
570  th->root_svar = sth->root_svar;
571  th->ensure_list = sth->ensure_list;
572 
573 }
574 
575 #if FIBER_USE_NATIVE
576 #ifdef _WIN32
577 static void
578 fiber_set_stack_location(void)
579 {
580  rb_thread_t *th = GET_THREAD();
581  VALUE *ptr;
582 
583  SET_MACHINE_STACK_END(&ptr);
584  th->machine.stack_start = (void*)(((VALUE)ptr & RB_PAGE_MASK) + STACK_UPPER((void *)&ptr, 0, RB_PAGE_SIZE));
585 }
586 
587 static VOID CALLBACK
588 fiber_entry(void *arg)
589 {
590  fiber_set_stack_location();
591  rb_fiber_start();
592 }
593 #else /* _WIN32 */
594 
595 /*
596  * FreeBSD require a first (i.e. addr) argument of mmap(2) is not NULL
597  * if MAP_STACK is passed.
598  * http://www.FreeBSD.org/cgi/query-pr.cgi?pr=158755
599  */
600 #if defined(MAP_STACK) && !defined(__FreeBSD__) && !defined(__FreeBSD_kernel__)
601 #define FIBER_STACK_FLAGS (MAP_PRIVATE | MAP_ANON | MAP_STACK)
602 #else
603 #define FIBER_STACK_FLAGS (MAP_PRIVATE | MAP_ANON)
604 #endif
605 
606 static char*
607 fiber_machine_stack_alloc(size_t size)
608 {
609  char *ptr;
610 
611  if (machine_stack_cache_index > 0) {
612  if (machine_stack_cache[machine_stack_cache_index - 1].size == (size / sizeof(VALUE))) {
613  ptr = machine_stack_cache[machine_stack_cache_index - 1].ptr;
614  machine_stack_cache_index--;
615  machine_stack_cache[machine_stack_cache_index].ptr = NULL;
616  machine_stack_cache[machine_stack_cache_index].size = 0;
617  }
618  else{
619  /* TODO handle multiple machine stack size */
620  rb_bug("machine_stack_cache size is not canonicalized");
621  }
622  }
623  else {
624  void *page;
626 
627  errno = 0;
628  ptr = mmap(NULL, size, PROT_READ | PROT_WRITE, FIBER_STACK_FLAGS, -1, 0);
629  if (ptr == MAP_FAILED) {
630  rb_raise(rb_eFiberError, "can't alloc machine stack to fiber: %s", strerror(errno));
631  }
632 
633  /* guard page setup */
634  page = ptr + STACK_DIR_UPPER(size - RB_PAGE_SIZE, 0);
635  if (mprotect(page, RB_PAGE_SIZE, PROT_NONE) < 0) {
636  rb_raise(rb_eFiberError, "mprotect failed");
637  }
638  }
639 
640  return ptr;
641 }
642 #endif
643 
644 static void
645 fiber_initialize_machine_stack_context(rb_fiber_t *fib, size_t size)
646 {
647  rb_thread_t *sth = &fib->cont.saved_thread;
648 
649 #ifdef _WIN32
650  fib->fib_handle = CreateFiberEx(size - 1, size, 0, fiber_entry, NULL);
651  if (!fib->fib_handle) {
652  /* try to release unnecessary fibers & retry to create */
653  rb_gc();
654  fib->fib_handle = CreateFiberEx(size - 1, size, 0, fiber_entry, NULL);
655  if (!fib->fib_handle) {
656  rb_raise(rb_eFiberError, "can't create fiber");
657  }
658  }
659  sth->machine.stack_maxsize = size;
660 #else /* not WIN32 */
661  ucontext_t *context = &fib->context;
662  char *ptr;
664 
665  getcontext(context);
666  ptr = fiber_machine_stack_alloc(size);
667  context->uc_link = NULL;
668  context->uc_stack.ss_sp = ptr;
669  context->uc_stack.ss_size = size;
670  makecontext(context, rb_fiber_start, 0);
671  sth->machine.stack_start = (VALUE*)(ptr + STACK_DIR_UPPER(0, size));
672  sth->machine.stack_maxsize = size - RB_PAGE_SIZE;
673 #endif
674 #ifdef __ia64
675  sth->machine.register_stack_maxsize = sth->machine.stack_maxsize;
676 #endif
677 }
678 
679 NOINLINE(static void fiber_setcontext(rb_fiber_t *newfib, rb_fiber_t *oldfib));
680 
681 static void
682 fiber_setcontext(rb_fiber_t *newfib, rb_fiber_t *oldfib)
683 {
684  rb_thread_t *th = GET_THREAD(), *sth = &newfib->cont.saved_thread;
685 
686  if (newfib->status != RUNNING) {
687  fiber_initialize_machine_stack_context(newfib, th->vm->default_params.fiber_machine_stack_size);
688  }
689 
690  /* restore thread context */
691  cont_restore_thread(&newfib->cont);
693  if (sth->machine.stack_end && (newfib != oldfib)) {
694  rb_bug("fiber_setcontext: sth->machine.stack_end has non zero value");
695  }
696 
697  /* save oldfib's machine stack */
698  if (oldfib->status != TERMINATED) {
701  if (STACK_DIR_UPPER(0, 1)) {
703  oldfib->cont.machine.stack = th->machine.stack_end;
704  }
705  else {
707  oldfib->cont.machine.stack = th->machine.stack_start;
708  }
709  }
710  /* exchange machine_stack_start between oldfib and newfib */
713  /* oldfib->machine.stack_end should be NULL */
714  oldfib->cont.saved_thread.machine.stack_end = 0;
715 #ifndef _WIN32
716  if (!newfib->context.uc_stack.ss_sp && th->root_fiber != newfib->cont.self) {
717  rb_bug("non_root_fiber->context.uc_stac.ss_sp should not be NULL");
718  }
719 #endif
720 
721  /* swap machine context */
722 #ifdef _WIN32
723  SwitchToFiber(newfib->fib_handle);
724 #else
725  swapcontext(&oldfib->context, &newfib->context);
726 #endif
727 }
728 #endif
729 
731 
732 static void
734 {
735  cont_restore_thread(cont);
736 
737  /* restore machine stack */
738 #ifdef _M_AMD64
739  {
740  /* workaround for x64 SEH */
741  jmp_buf buf;
742  setjmp(buf);
743  ((_JUMP_BUFFER*)(&cont->jmpbuf))->Frame =
744  ((_JUMP_BUFFER*)(&buf))->Frame;
745  }
746 #endif
747  if (cont->machine.stack_src) {
749  MEMCPY(cont->machine.stack_src, cont->machine.stack,
750  VALUE, cont->machine.stack_size);
751  }
752 
753 #ifdef __ia64
754  if (cont->machine.register_stack_src) {
755  MEMCPY(cont->machine.register_stack_src, cont->machine.register_stack,
756  VALUE, cont->machine.register_stack_size);
757  }
758 #endif
759 
760  ruby_longjmp(cont->jmpbuf, 1);
761 }
762 
764 
765 #ifdef __ia64
766 #define C(a) rse_##a##0, rse_##a##1, rse_##a##2, rse_##a##3, rse_##a##4
767 #define E(a) rse_##a##0= rse_##a##1= rse_##a##2= rse_##a##3= rse_##a##4
768 static volatile int C(a), C(b), C(c), C(d), C(e);
769 static volatile int C(f), C(g), C(h), C(i), C(j);
770 static volatile int C(k), C(l), C(m), C(n), C(o);
771 static volatile int C(p), C(q), C(r), C(s), C(t);
772 #if 0
773 {/* the above lines make cc-mode.el confused so much */}
774 #endif
775 int rb_dummy_false = 0;
776 NORETURN(NOINLINE(static void register_stack_extend(rb_context_t *, VALUE *, VALUE *)));
777 static void
778 register_stack_extend(rb_context_t *cont, VALUE *vp, VALUE *curr_bsp)
779 {
780  if (rb_dummy_false) {
781  /* use registers as much as possible */
782  E(a) = E(b) = E(c) = E(d) = E(e) =
783  E(f) = E(g) = E(h) = E(i) = E(j) =
784  E(k) = E(l) = E(m) = E(n) = E(o) =
785  E(p) = E(q) = E(r) = E(s) = E(t) = 0;
786  E(a) = E(b) = E(c) = E(d) = E(e) =
787  E(f) = E(g) = E(h) = E(i) = E(j) =
788  E(k) = E(l) = E(m) = E(n) = E(o) =
789  E(p) = E(q) = E(r) = E(s) = E(t) = 0;
790  }
791  if (curr_bsp < cont->machine.register_stack_src+cont->machine.register_stack_size) {
792  register_stack_extend(cont, vp, (VALUE*)rb_ia64_bsp());
793  }
794  cont_restore_0(cont, vp);
795 }
796 #undef C
797 #undef E
798 #endif
799 
800 static void
801 cont_restore_0(rb_context_t *cont, VALUE *addr_in_prev_frame)
802 {
803  if (cont->machine.stack_src) {
804 #ifdef HAVE_ALLOCA
805 #define STACK_PAD_SIZE 1
806 #else
807 #define STACK_PAD_SIZE 1024
808 #endif
809  VALUE space[STACK_PAD_SIZE];
810 
811 #if !STACK_GROW_DIRECTION
812  if (addr_in_prev_frame > &space[0]) {
813  /* Stack grows downward */
814 #endif
815 #if STACK_GROW_DIRECTION <= 0
816  volatile VALUE *const end = cont->machine.stack_src;
817  if (&space[0] > end) {
818 # ifdef HAVE_ALLOCA
819  volatile VALUE *sp = ALLOCA_N(VALUE, &space[0] - end);
820  space[0] = *sp;
821 # else
822  cont_restore_0(cont, &space[0]);
823 # endif
824  }
825 #endif
826 #if !STACK_GROW_DIRECTION
827  }
828  else {
829  /* Stack grows upward */
830 #endif
831 #if STACK_GROW_DIRECTION >= 0
832  volatile VALUE *const end = cont->machine.stack_src + cont->machine.stack_size;
833  if (&space[STACK_PAD_SIZE] < end) {
834 # ifdef HAVE_ALLOCA
835  volatile VALUE *sp = ALLOCA_N(VALUE, end - &space[STACK_PAD_SIZE]);
836  space[0] = *sp;
837 # else
838  cont_restore_0(cont, &space[STACK_PAD_SIZE-1]);
839 # endif
840  }
841 #endif
842 #if !STACK_GROW_DIRECTION
843  }
844 #endif
845  }
846  cont_restore_1(cont);
847 }
848 #ifdef __ia64
849 #define cont_restore_0(cont, vp) register_stack_extend((cont), (vp), (VALUE*)rb_ia64_bsp())
850 #endif
851 
852 /*
853  * Document-class: Continuation
854  *
855  * Continuation objects are generated by Kernel#callcc,
856  * after having +require+d <i>continuation</i>. They hold
857  * a return address and execution context, allowing a nonlocal return
858  * to the end of the <code>callcc</code> block from anywhere within a
859  * program. Continuations are somewhat analogous to a structured
860  * version of C's <code>setjmp/longjmp</code> (although they contain
861  * more state, so you might consider them closer to threads).
862  *
863  * For instance:
864  *
865  * require "continuation"
866  * arr = [ "Freddie", "Herbie", "Ron", "Max", "Ringo" ]
867  * callcc{|cc| $cc = cc}
868  * puts(message = arr.shift)
869  * $cc.call unless message =~ /Max/
870  *
871  * <em>produces:</em>
872  *
873  * Freddie
874  * Herbie
875  * Ron
876  * Max
877  *
878  * This (somewhat contrived) example allows the inner loop to abandon
879  * processing early:
880  *
881  * require "continuation"
882  * callcc {|cont|
883  * for i in 0..4
884  * print "\n#{i}: "
885  * for j in i*5...(i+1)*5
886  * cont.call() if j == 17
887  * printf "%3d", j
888  * end
889  * end
890  * }
891  * puts
892  *
893  * <em>produces:</em>
894  *
895  * 0: 0 1 2 3 4
896  * 1: 5 6 7 8 9
897  * 2: 10 11 12 13 14
898  * 3: 15 16
899  */
900 
901 /*
902  * call-seq:
903  * callcc {|cont| block } -> obj
904  *
905  * Generates a Continuation object, which it passes to
906  * the associated block. You need to <code>require
907  * 'continuation'</code> before using this method. Performing a
908  * <em>cont</em><code>.call</code> will cause the #callcc
909  * to return (as will falling through the end of the block). The
910  * value returned by the #callcc is the value of the
911  * block, or the value passed to <em>cont</em><code>.call</code>. See
912  * class Continuation for more details. Also see
913  * Kernel#throw for an alternative mechanism for
914  * unwinding a call stack.
915  */
916 
917 static VALUE
919 {
920  volatile int called;
921  volatile VALUE val = cont_capture(&called);
922 
923  if (called) {
924  return val;
925  }
926  else {
927  return rb_yield(val);
928  }
929 }
930 
931 static VALUE
933 {
934  switch (argc) {
935  case 0:
936  return Qnil;
937  case 1:
938  return argv[0];
939  default:
940  return rb_ary_new4(argc, argv);
941  }
942 }
943 
944 /* CAUTION!! : Currently, error in rollback_func is not supported */
945 /* same as rb_protect if set rollback_func to NULL */
946 void
948 {
949  st_table **table_p = &GET_VM()->ensure_rollback_table;
950  if (UNLIKELY(*table_p == NULL)) {
951  *table_p = st_init_numtable();
952  }
953  st_insert(*table_p, (st_data_t)ensure_func, (st_data_t)rollback_func);
954 }
955 
956 static inline VALUE
958 {
959  st_table *table = GET_VM()->ensure_rollback_table;
960  st_data_t val;
961  if (table && st_lookup(table, (st_data_t)ensure_func, &val))
962  return (VALUE) val;
963  return Qundef;
964 }
965 
966 
967 static inline void
969 {
970  rb_ensure_list_t *p;
971  rb_ensure_entry_t *entry;
972  size_t i;
973  size_t cur_size;
974  size_t target_size;
975  size_t base_point;
976  VALUE (*func)(ANYARGS);
977 
978  cur_size = 0;
979  for (p=current; p; p=p->next)
980  cur_size++;
981  target_size = 0;
982  for (entry=target; entry->marker; entry++)
983  target_size++;
984 
985  /* search common stack point */
986  p = current;
987  base_point = cur_size;
988  while (base_point) {
989  if (target_size >= base_point &&
990  p->entry.marker == target[target_size - base_point].marker)
991  break;
992  base_point --;
993  p = p->next;
994  }
995 
996  /* rollback function check */
997  for (i=0; i < target_size - base_point; i++) {
998  if (!lookup_rollback_func(target[i].e_proc)) {
999  rb_raise(rb_eRuntimeError, "continuation called from out of critical rb_ensure scope");
1000  }
1001  }
1002  /* pop ensure stack */
1003  while (cur_size > base_point) {
1004  /* escape from ensure block */
1005  (*current->entry.e_proc)(current->entry.data2);
1006  current = current->next;
1007  cur_size--;
1008  }
1009  /* push ensure stack */
1010  while (i--) {
1011  func = (VALUE (*)(ANYARGS)) lookup_rollback_func(target[i].e_proc);
1012  if ((VALUE)func != Qundef) {
1013  (*func)(target[i].data2);
1014  }
1015  }
1016 }
1017 
1018 /*
1019  * call-seq:
1020  * cont.call(args, ...)
1021  * cont[args, ...]
1022  *
1023  * Invokes the continuation. The program continues from the end of the
1024  * <code>callcc</code> block. If no arguments are given, the original
1025  * <code>callcc</code> returns <code>nil</code>. If one argument is
1026  * given, <code>callcc</code> returns it. Otherwise, an array
1027  * containing <i>args</i> is returned.
1028  *
1029  * callcc {|cont| cont.call } #=> nil
1030  * callcc {|cont| cont.call 1 } #=> 1
1031  * callcc {|cont| cont.call 1, 2, 3 } #=> [1, 2, 3]
1032  */
1033 
1034 static VALUE
1036 {
1037  rb_context_t *cont;
1038  rb_thread_t *th = GET_THREAD();
1039  GetContPtr(contval, cont);
1040 
1041  if (cont->saved_thread.self != th->self) {
1042  rb_raise(rb_eRuntimeError, "continuation called across threads");
1043  }
1044  if (cont->saved_thread.protect_tag != th->protect_tag) {
1045  rb_raise(rb_eRuntimeError, "continuation called across stack rewinding barrier");
1046  }
1047  if (cont->saved_thread.fiber) {
1048  rb_fiber_t *fcont;
1049  GetFiberPtr(cont->saved_thread.fiber, fcont);
1050 
1051  if (th->fiber != cont->saved_thread.fiber) {
1052  rb_raise(rb_eRuntimeError, "continuation called across fiber");
1053  }
1054  }
1055  rollback_ensure_stack(contval, th->ensure_list, cont->ensure_array);
1056 
1057  cont->argc = argc;
1058  cont->value = make_passing_arg(argc, argv);
1059 
1060  /* restore `tracing' context. see [Feature #4347] */
1061  th->trace_arg = cont->saved_thread.trace_arg;
1062 
1063  cont_restore_0(cont, &contval);
1064  return Qnil; /* unreachable */
1065 }
1066 
1067 /*********/
1068 /* fiber */
1069 /*********/
1070 
1071 /*
1072  * Document-class: Fiber
1073  *
1074  * Fibers are primitives for implementing light weight cooperative
1075  * concurrency in Ruby. Basically they are a means of creating code blocks
1076  * that can be paused and resumed, much like threads. The main difference
1077  * is that they are never preempted and that the scheduling must be done by
1078  * the programmer and not the VM.
1079  *
1080  * As opposed to other stackless light weight concurrency models, each fiber
1081  * comes with a small 4KB stack. This enables the fiber to be paused from deeply
1082  * nested function calls within the fiber block.
1083  *
1084  * When a fiber is created it will not run automatically. Rather it must be
1085  * be explicitly asked to run using the <code>Fiber#resume</code> method.
1086  * The code running inside the fiber can give up control by calling
1087  * <code>Fiber.yield</code> in which case it yields control back to caller
1088  * (the caller of the <code>Fiber#resume</code>).
1089  *
1090  * Upon yielding or termination the Fiber returns the value of the last
1091  * executed expression
1092  *
1093  * For instance:
1094  *
1095  * fiber = Fiber.new do
1096  * Fiber.yield 1
1097  * 2
1098  * end
1099  *
1100  * puts fiber.resume
1101  * puts fiber.resume
1102  * puts fiber.resume
1103  *
1104  * <em>produces</em>
1105  *
1106  * 1
1107  * 2
1108  * FiberError: dead fiber called
1109  *
1110  * The <code>Fiber#resume</code> method accepts an arbitrary number of
1111  * parameters, if it is the first call to <code>resume</code> then they
1112  * will be passed as block arguments. Otherwise they will be the return
1113  * value of the call to <code>Fiber.yield</code>
1114  *
1115  * Example:
1116  *
1117  * fiber = Fiber.new do |first|
1118  * second = Fiber.yield first + 2
1119  * end
1120  *
1121  * puts fiber.resume 10
1122  * puts fiber.resume 14
1123  * puts fiber.resume 18
1124  *
1125  * <em>produces</em>
1126  *
1127  * 12
1128  * 14
1129  * FiberError: dead fiber called
1130  *
1131  */
1132 
1133 static const rb_data_type_t fiber_data_type = {
1134  "fiber",
1137 };
1138 
1139 static VALUE
1141 {
1142  return TypedData_Wrap_Struct(klass, &fiber_data_type, 0);
1143 }
1144 
1145 static rb_fiber_t*
1147 {
1148  rb_fiber_t *fib;
1149  rb_thread_t *th = GET_THREAD();
1150 
1151  if (DATA_PTR(fibval) != 0) {
1152  rb_raise(rb_eRuntimeError, "cannot initialize twice");
1153  }
1154 
1156  fib = ALLOC(rb_fiber_t);
1157  memset(fib, 0, sizeof(rb_fiber_t));
1158  fib->cont.self = fibval;
1159  fib->cont.type = FIBER_CONTEXT;
1160  cont_init(&fib->cont, th);
1161  fib->prev = Qnil;
1162  fib->status = CREATED;
1163 
1164  DATA_PTR(fibval) = fib;
1165 
1166  return fib;
1167 }
1168 
1169 static VALUE
1170 fiber_init(VALUE fibval, VALUE proc)
1171 {
1172  rb_fiber_t *fib = fiber_t_alloc(fibval);
1173  rb_context_t *cont = &fib->cont;
1174  rb_thread_t *th = &cont->saved_thread;
1175 
1176  /* initialize cont */
1177  cont->vm_stack = 0;
1178 
1179  th->stack = 0;
1180  th->stack_size = 0;
1181 
1182  fiber_link_join(fib);
1183 
1184  th->stack_size = th->vm->default_params.fiber_vm_stack_size / sizeof(VALUE);
1185  th->stack = ALLOC_N(VALUE, th->stack_size);
1186 
1187  th->cfp = (void *)(th->stack + th->stack_size);
1188  th->cfp--;
1189  th->cfp->pc = 0;
1190  th->cfp->sp = th->stack + 1;
1191 #if VM_DEBUG_BP_CHECK
1192  th->cfp->bp_check = 0;
1193 #endif
1194  th->cfp->ep = th->stack;
1195  *th->cfp->ep = VM_ENVVAL_BLOCK_PTR(0);
1196  th->cfp->self = Qnil;
1197  th->cfp->klass = Qnil;
1198  th->cfp->flag = 0;
1199  th->cfp->iseq = 0;
1200  th->cfp->proc = 0;
1201  th->cfp->block_iseq = 0;
1202  th->cfp->me = 0;
1203  th->tag = 0;
1205 
1206  th->first_proc = proc;
1207 
1208 #if !FIBER_USE_NATIVE
1209  MEMCPY(&cont->jmpbuf, &th->root_jmpbuf, rb_jmpbuf_t, 1);
1210 #endif
1211 
1212  return fibval;
1213 }
1214 
1215 /* :nodoc: */
1216 static VALUE
1218 {
1219  return fiber_init(fibval, rb_block_proc());
1220 }
1221 
1222 VALUE
1224 {
1226 }
1227 
1228 static VALUE
1230 {
1231  rb_fiber_t *fib;
1232  VALUE curr = rb_fiber_current();
1233  VALUE prev;
1234  GetFiberPtr(curr, fib);
1235 
1236  prev = fib->prev;
1237  if (NIL_P(prev)) {
1238  const VALUE root_fiber = GET_THREAD()->root_fiber;
1239 
1240  if (root_fiber == curr) {
1241  rb_raise(rb_eFiberError, "can't yield from root fiber");
1242  }
1243  return root_fiber;
1244  }
1245  else {
1246  fib->prev = Qnil;
1247  return prev;
1248  }
1249 }
1250 
1252 
1253 static void
1255 {
1256  VALUE value = fib->cont.value;
1257  fib->status = TERMINATED;
1258 #if FIBER_USE_NATIVE && !defined(_WIN32)
1259  /* Ruby must not switch to other thread until storing terminated_machine_stack */
1260  terminated_machine_stack.ptr = fib->context.uc_stack.ss_sp;
1261  terminated_machine_stack.size = fib->context.uc_stack.ss_size / sizeof(VALUE);
1262  fib->context.uc_stack.ss_sp = NULL;
1263  fib->cont.machine.stack = NULL;
1264  fib->cont.machine.stack_size = 0;
1265 #endif
1266  rb_fiber_transfer(return_fiber(), 1, &value);
1267 }
1268 
1269 void
1271 {
1272  rb_thread_t *th = GET_THREAD();
1273  rb_fiber_t *fib;
1274  rb_context_t *cont;
1275  rb_proc_t *proc;
1276  int state;
1277 
1278  GetFiberPtr(th->fiber, fib);
1279  cont = &fib->cont;
1280 
1281  TH_PUSH_TAG(th);
1282  if ((state = EXEC_TAG()) == 0) {
1283  int argc;
1284  const VALUE *argv, args = cont->value;
1285  GetProcPtr(cont->saved_thread.first_proc, proc);
1286  argv = (argc = cont->argc) > 1 ? RARRAY_CONST_PTR(args) : &args;
1287  cont->value = Qnil;
1288  th->errinfo = Qnil;
1289  th->root_lep = rb_vm_ep_local_ep(proc->block.ep);
1290  th->root_svar = Qnil;
1291 
1292  fib->status = RUNNING;
1293  cont->value = rb_vm_invoke_proc(th, proc, argc, argv, 0);
1294  }
1295  TH_POP_TAG();
1296 
1297  if (state) {
1298  if (state == TAG_RAISE || state == TAG_FATAL) {
1300  }
1301  else {
1303  if (!NIL_P(err))
1305  }
1307  }
1308 
1309  rb_fiber_terminate(fib);
1310  rb_bug("rb_fiber_start: unreachable");
1311 }
1312 
1313 static rb_fiber_t *
1315 {
1316  rb_fiber_t *fib;
1317  /* no need to allocate vm stack */
1319  fib->cont.type = ROOT_FIBER_CONTEXT;
1320 #if FIBER_USE_NATIVE
1321 #ifdef _WIN32
1322  fib->fib_handle = ConvertThreadToFiber(0);
1323 #endif
1324 #endif
1325  fib->status = RUNNING;
1326  fib->prev_fiber = fib->next_fiber = fib;
1327 
1328  return fib;
1329 }
1330 
1331 VALUE
1333 {
1334  rb_thread_t *th = GET_THREAD();
1335  if (th->fiber == 0) {
1336  /* save root */
1337  rb_fiber_t *fib = root_fiber_alloc(th);
1338  th->root_fiber = th->fiber = fib->cont.self;
1339  }
1340  return th->fiber;
1341 }
1342 
1343 static VALUE
1345 {
1346  rb_thread_t *th = GET_THREAD();
1347  rb_fiber_t *fib;
1348 
1349  if (th->fiber) {
1350  GetFiberPtr(th->fiber, fib);
1351  cont_save_thread(&fib->cont, th);
1352  }
1353  else {
1354  /* create current fiber */
1355  fib = root_fiber_alloc(th);
1356  th->root_fiber = th->fiber = fib->cont.self;
1357  }
1358 
1359 #if !FIBER_USE_NATIVE
1360  cont_save_machine_stack(th, &fib->cont);
1361 #endif
1362 
1363  if (FIBER_USE_NATIVE || ruby_setjmp(fib->cont.jmpbuf)) {
1364 #if FIBER_USE_NATIVE
1365  fiber_setcontext(next_fib, fib);
1366 #ifndef _WIN32
1367  if (terminated_machine_stack.ptr) {
1368  if (machine_stack_cache_index < MAX_MACHINE_STACK_CACHE) {
1369  machine_stack_cache[machine_stack_cache_index].ptr = terminated_machine_stack.ptr;
1370  machine_stack_cache[machine_stack_cache_index].size = terminated_machine_stack.size;
1371  machine_stack_cache_index++;
1372  }
1373  else {
1374  if (terminated_machine_stack.ptr != fib->cont.machine.stack) {
1375  munmap((void*)terminated_machine_stack.ptr, terminated_machine_stack.size * sizeof(VALUE));
1376  }
1377  else {
1378  rb_bug("terminated fiber resumed");
1379  }
1380  }
1381  terminated_machine_stack.ptr = NULL;
1382  terminated_machine_stack.size = 0;
1383  }
1384 #endif
1385 #endif
1386  /* restored */
1387  GetFiberPtr(th->fiber, fib);
1388  if (fib->cont.argc == -1) rb_exc_raise(fib->cont.value);
1389  return fib->cont.value;
1390  }
1391 #if !FIBER_USE_NATIVE
1392  else {
1393  return Qundef;
1394  }
1395 #endif
1396 }
1397 
1398 static inline VALUE
1399 fiber_switch(VALUE fibval, int argc, VALUE *argv, int is_resume)
1400 {
1401  VALUE value;
1402  rb_fiber_t *fib;
1403  rb_context_t *cont;
1404  rb_thread_t *th = GET_THREAD();
1405 
1406  GetFiberPtr(fibval, fib);
1407  cont = &fib->cont;
1408 
1409  if (th->fiber == fibval) {
1410  /* ignore fiber context switch
1411  * because destination fiber is same as current fiber
1412  */
1413  return make_passing_arg(argc, argv);
1414  }
1415 
1416  if (cont->saved_thread.self != th->self) {
1417  rb_raise(rb_eFiberError, "fiber called across threads");
1418  }
1419  else if (cont->saved_thread.protect_tag != th->protect_tag) {
1420  rb_raise(rb_eFiberError, "fiber called across stack rewinding barrier");
1421  }
1422  else if (fib->status == TERMINATED) {
1423  value = rb_exc_new2(rb_eFiberError, "dead fiber called");
1424  if (th->fiber != fibval) {
1425  GetFiberPtr(th->fiber, fib);
1426  if (fib->status != TERMINATED) rb_exc_raise(value);
1427  fibval = th->root_fiber;
1428  }
1429  else {
1430  fibval = fib->prev;
1431  if (NIL_P(fibval)) fibval = th->root_fiber;
1432  }
1433  GetFiberPtr(fibval, fib);
1434  cont = &fib->cont;
1435  cont->argc = -1;
1436  cont->value = value;
1437 #if FIBER_USE_NATIVE
1438  {
1439  VALUE oldfibval;
1440  rb_fiber_t *oldfib;
1441  oldfibval = rb_fiber_current();
1442  GetFiberPtr(oldfibval, oldfib);
1443  fiber_setcontext(fib, oldfib);
1444  }
1445 #else
1446  cont_restore_0(cont, &value);
1447 #endif
1448  }
1449 
1450  if (is_resume) {
1451  fib->prev = rb_fiber_current();
1452  }
1453  else {
1454  /* restore `tracing' context. see [Feature #4347] */
1455  th->trace_arg = cont->saved_thread.trace_arg;
1456  }
1457 
1458  cont->argc = argc;
1459  cont->value = make_passing_arg(argc, argv);
1460 
1461  value = fiber_store(fib);
1462 #if !FIBER_USE_NATIVE
1463  if (value == Qundef) {
1464  cont_restore_0(cont, &value);
1465  rb_bug("rb_fiber_resume: unreachable");
1466  }
1467 #endif
1468  RUBY_VM_CHECK_INTS(th);
1469 
1470  return value;
1471 }
1472 
1473 VALUE
1475 {
1476  return fiber_switch(fib, argc, argv, 0);
1477 }
1478 
1479 VALUE
1481 {
1482  rb_fiber_t *fib;
1483  GetFiberPtr(fibval, fib);
1484 
1485  if (fib->prev != Qnil || fib->cont.type == ROOT_FIBER_CONTEXT) {
1486  rb_raise(rb_eFiberError, "double resume");
1487  }
1488  if (fib->transfered != 0) {
1489  rb_raise(rb_eFiberError, "cannot resume transferred Fiber");
1490  }
1491 
1492  return fiber_switch(fibval, argc, argv, 1);
1493 }
1494 
1495 VALUE
1497 {
1498  return rb_fiber_transfer(return_fiber(), argc, argv);
1499 }
1500 
1501 void
1503 {
1504  rb_thread_t *th;
1505  rb_fiber_t *fib;
1506 
1507  GetThreadPtr(thval, th);
1508  if (th->root_fiber && th->root_fiber != th->fiber) {
1509  GetFiberPtr(th->root_fiber, fib);
1511  }
1512 }
1513 
1514 /*
1515  * call-seq:
1516  * fiber.alive? -> true or false
1517  *
1518  * Returns true if the fiber can still be resumed (or transferred
1519  * to). After finishing execution of the fiber block this method will
1520  * always return false. You need to <code>require 'fiber'</code>
1521  * before using this method.
1522  */
1523 VALUE
1525 {
1526  rb_fiber_t *fib;
1527  GetFiberPtr(fibval, fib);
1528  return fib->status != TERMINATED ? Qtrue : Qfalse;
1529 }
1530 
1531 /*
1532  * call-seq:
1533  * fiber.resume(args, ...) -> obj
1534  *
1535  * Resumes the fiber from the point at which the last <code>Fiber.yield</code>
1536  * was called, or starts running it if it is the first call to
1537  * <code>resume</code>. Arguments passed to resume will be the value of
1538  * the <code>Fiber.yield</code> expression or will be passed as block
1539  * parameters to the fiber's block if this is the first <code>resume</code>.
1540  *
1541  * Alternatively, when resume is called it evaluates to the arguments passed
1542  * to the next <code>Fiber.yield</code> statement inside the fiber's block
1543  * or to the block value if it runs to completion without any
1544  * <code>Fiber.yield</code>
1545  */
1546 static VALUE
1548 {
1549  return rb_fiber_resume(fib, argc, argv);
1550 }
1551 
1552 /*
1553  * call-seq:
1554  * fiber.transfer(args, ...) -> obj
1555  *
1556  * Transfer control to another fiber, resuming it from where it last
1557  * stopped or starting it if it was not resumed before. The calling
1558  * fiber will be suspended much like in a call to
1559  * <code>Fiber.yield</code>. You need to <code>require 'fiber'</code>
1560  * before using this method.
1561  *
1562  * The fiber which receives the transfer call is treats it much like
1563  * a resume call. Arguments passed to transfer are treated like those
1564  * passed to resume.
1565  *
1566  * You cannot resume a fiber that transferred control to another one.
1567  * This will cause a double resume error. You need to transfer control
1568  * back to this fiber before it can yield and resume.
1569  *
1570  * Example:
1571  *
1572  * fiber1 = Fiber.new do
1573  * puts "In Fiber 1"
1574  * Fiber.yield
1575  * end
1576  *
1577  * fiber2 = Fiber.new do
1578  * puts "In Fiber 2"
1579  * fiber1.transfer
1580  * puts "Never see this message"
1581  * end
1582  *
1583  * fiber3 = Fiber.new do
1584  * puts "In Fiber 3"
1585  * end
1586  *
1587  * fiber2.resume
1588  * fiber3.resume
1589  *
1590  * <em>produces</em>
1591  *
1592  * In fiber 2
1593  * In fiber 1
1594  * In fiber 3
1595  *
1596  */
1597 static VALUE
1599 {
1600  rb_fiber_t *fib;
1601  GetFiberPtr(fibval, fib);
1602  fib->transfered = 1;
1603  return rb_fiber_transfer(fibval, argc, argv);
1604 }
1605 
1606 /*
1607  * call-seq:
1608  * Fiber.yield(args, ...) -> obj
1609  *
1610  * Yields control back to the context that resumed the fiber, passing
1611  * along any arguments that were passed to it. The fiber will resume
1612  * processing at this point when <code>resume</code> is called next.
1613  * Any arguments passed to the next <code>resume</code> will be the
1614  * value that this <code>Fiber.yield</code> expression evaluates to.
1615  */
1616 static VALUE
1618 {
1619  return rb_fiber_yield(argc, argv);
1620 }
1621 
1622 /*
1623  * call-seq:
1624  * Fiber.current() -> fiber
1625  *
1626  * Returns the current fiber. You need to <code>require 'fiber'</code>
1627  * before using this method. If you are not running in the context of
1628  * a fiber this method will return the root fiber.
1629  */
1630 static VALUE
1632 {
1633  return rb_fiber_current();
1634 }
1635 
1636 
1637 
1638 /*
1639  * Document-class: FiberError
1640  *
1641  * Raised when an invalid operation is attempted on a Fiber, in
1642  * particular when attempting to call/resume a dead fiber,
1643  * attempting to yield from the root fiber, or calling a fiber across
1644  * threads.
1645  *
1646  * fiber = Fiber.new{}
1647  * fiber.resume #=> nil
1648  * fiber.resume #=> FiberError: dead fiber called
1649  */
1650 
1651 void
1653 {
1654 #if FIBER_USE_NATIVE
1655  rb_thread_t *th = GET_THREAD();
1656 
1657 #ifdef _WIN32
1658  SYSTEM_INFO info;
1659  GetSystemInfo(&info);
1660  pagesize = info.dwPageSize;
1661 #else /* not WIN32 */
1662  pagesize = sysconf(_SC_PAGESIZE);
1663 #endif
1665 #endif
1666 
1667  rb_cFiber = rb_define_class("Fiber", rb_cObject);
1671  rb_define_method(rb_cFiber, "initialize", rb_fiber_init, 0);
1673 }
1674 
1676 
1677 void
1679 {
1680  rb_cContinuation = rb_define_class("Continuation", rb_cObject);
1685  rb_define_global_function("callcc", rb_callcc, 0);
1686 }
1687 
1688 void
1690 {
1694 }
1695 
void rb_gc(void)
Definition: gc.c:5190
rb_control_frame_t * cfp
Definition: vm_core.h:531
VALUE * vm_stack
Definition: cont.c:95
VALUE rb_eStandardError
Definition: error.c:546
VALUE * stack_end
Definition: vm_core.h:622
#define RUBY_VM_CHECK_INTS(th)
Definition: vm_core.h:986
rb_vm_t * vm
Definition: vm_core.h:526
struct rb_ensure_entry entry
Definition: vm_core.h:521
#define rb_exc_new2
Definition: intern.h:247
#define THREAD_MUST_BE_RUNNING(th)
Definition: cont.c:169
#define GetContPtr(obj, ptr)
Definition: cont.c:159
void rb_bug(const char *fmt,...)
Definition: error.c:327
VALUE * root_lep
Definition: vm_core.h:557
#define ruby_longjmp(env, val)
Definition: eval_intern.h:51
#define RUBY_TYPED_FREE_IMMEDIATELY
Definition: ruby.h:1015
struct rb_vm_protect_tag * protect_tag
Definition: vm_core.h:594
#define rb_gc_mark_locations(start, end)
Definition: gc.c:3316
static VALUE rb_cContinuation
Definition: cont.c:155
Definition: st.h:69
#define RUBY_VM_SET_INTERRUPT(th)
Definition: vm_core.h:957
st_table * local_storage
Definition: vm_core.h:611
void rb_undef_alloc_func(VALUE)
Definition: vm_method.c:518
void rb_define_singleton_method(VALUE obj, const char *name, VALUE(*func)(ANYARGS), int argc)
Defines a singleton method for obj.
Definition: class.c:1655
#define GetProcPtr(obj, ptr)
Definition: vm_core.h:697
VALUE self
Definition: cont.c:92
#define FLUSH_REGISTER_WINDOWS
Definition: defines.h:270
#define CLASS_OF(v)
Definition: ruby.h:440
static VALUE cont_capture(volatile int *stat)
Definition: cont.c:468
static VALUE lookup_rollback_func(VALUE(*ensure_func)(ANYARGS))
Definition: cont.c:957
#define Qtrue
Definition: ruby.h:426
int st_insert(st_table *, st_data_t, st_data_t)
#define TypedData_Wrap_Struct(klass, data_type, sval)
Definition: ruby.h:1027
void rb_fiber_reset_root_local_storage(VALUE thval)
Definition: cont.c:1502
VALUE prev
Definition: cont.c:135
static VALUE rb_fiber_m_resume(int argc, VALUE *argv, VALUE fib)
Definition: cont.c:1547
static const rb_data_type_t cont_data_type
Definition: cont.c:154
#define GetFiberPtr(obj, ptr)
Definition: cont.c:162
VALUE data2
Definition: vm_core.h:516
static rb_fiber_t * root_fiber_alloc(rb_thread_t *th)
Definition: cont.c:1314
enum context_type type
Definition: cont.c:91
static void rb_fiber_terminate(rb_fiber_t *fib)
Definition: cont.c:1254
void st_free_table(st_table *)
Definition: st.c:334
VALUE rb_fiber_yield(int argc, VALUE *argv)
Definition: cont.c:1496
size_t fiber_machine_stack_size
Definition: vm_core.h:423
VALUE rb_ary_tmp_new(long capa)
Definition: array.c:534
static VALUE make_passing_arg(int argc, VALUE *argv)
Definition: cont.c:932
int transfered
Definition: cont.c:143
#define VM_ENVVAL_BLOCK_PTR(v)
Definition: vm_core.h:812
#define STACK_UPPER(x, a, b)
Definition: gc.h:74
VALUE rb_fiber_alive_p(VALUE fibval)
Definition: cont.c:1524
static VALUE fiber_switch(VALUE fibval, int argc, VALUE *argv, int is_resume)
Definition: cont.c:1399
void rb_threadptr_pending_interrupt_enque(rb_thread_t *th, VALUE v)
Definition: thread.c:1544
void rb_raise(VALUE exc, const char *fmt,...)
Definition: error.c:1854
static void cont_save_thread(rb_context_t *cont, rb_thread_t *th)
Definition: cont.c:431
#define RUBY_MARK_LEAVE(msg)
Definition: gc.h:54
VALUE rb_fiber_current(void)
Definition: cont.c:1332
#define C
Definition: util.c:195
void rb_define_alloc_func(VALUE, rb_alloc_func_t)
static VALUE fiber_init(VALUE fibval, VALUE proc)
Definition: cont.c:1170
#define DATA_PTR(dta)
Definition: ruby.h:992
void rb_gc_mark(VALUE ptr)
Definition: gc.c:3604
static void fiber_link_join(rb_fiber_t *fib)
Definition: cont.c:316
#define TAG_RAISE
Definition: eval_intern.h:193
rb_jmpbuf_t jmpbuf
Definition: cont.c:111
void rb_define_global_function(const char *name, VALUE(*func)(ANYARGS), int argc)
Defines a global function.
Definition: class.c:1684
VALUE rb_vm_invoke_proc(rb_thread_t *th, rb_proc_t *proc, int argc, const VALUE *argv, const rb_block_t *blockptr)
Definition: vm.c:862
Definition: vm_core.h:513
static const rb_data_type_t fiber_data_type
Definition: cont.c:154
VALUE value
Definition: cont.c:94
void rb_undef_method(VALUE klass, const char *name)
Definition: class.c:1506
struct rb_thread_struct::@169 machine
static void cont_free(void *ptr)
Definition: cont.c:221
struct rb_context_struct::@2 machine
rb_thread_t saved_thread
Definition: cont.c:110
struct rb_context_struct rb_context_t
static VALUE rb_eFiberError
Definition: cont.c:157
VALUE * stack
Definition: cont.c:101
RUBY_SYMBOL_EXPORT_BEGIN typedef unsigned long st_data_t
Definition: st.h:20
#define TAG_FATAL
Definition: eval_intern.h:195
void rb_exc_raise(VALUE mesg)
Definition: eval.c:567
size_t vm_stack_clen
Definition: cont.c:98
VALUE * stack
Definition: vm_core.h:529
static size_t fiber_memsize(const void *ptr)
Definition: cont.c:354
#define TH_POP_TAG()
Definition: eval_intern.h:128
int st_lookup(st_table *, st_data_t, st_data_t *)
enum fiber_status status
Definition: cont.c:136
VALUE rb_fiber_resume(VALUE fibval, int argc, VALUE *argv)
Definition: cont.c:1480
static VALUE return_fiber(void)
Definition: cont.c:1229
#define ALLOC_N(type, n)
Definition: ruby.h:1333
#define EXEC_TAG()
Definition: eval_intern.h:168
#define val
VALUE * rb_vm_ep_local_ep(VALUE *ep)
Definition: vm.c:34
rb_iseq_t * block_iseq
Definition: vm_core.h:453
RUBY_EXTERN VALUE rb_cObject
Definition: ruby.h:1553
VALUE rb_eRuntimeError
Definition: error.c:547
rb_ensure_entry_t * ensure_array
Definition: cont.c:112
size_t fiber_vm_stack_size
Definition: vm_core.h:422
int rb_typeddata_is_kind_of(VALUE obj, const rb_data_type_t *data_type)
Definition: error.c:510
Definition: cont.c:117
static VALUE rb_fiber_init(VALUE fibval)
Definition: cont.c:1217
static void cont_restore_1(rb_context_t *cont)
Definition: cont.c:733
rb_iseq_t * iseq
Definition: vm_core.h:448
#define NIL_P(v)
Definition: ruby.h:438
#define UNLIKELY(x)
Definition: vm_core.h:109
VALUE rb_define_class(const char *name, VALUE super)
Defines a top-level class.
Definition: class.c:630
static VALUE fiber_alloc(VALUE klass)
Definition: cont.c:1140
VALUE tag
Definition: vm_core.h:489
#define RUBY_MARK_ENTER(msg)
Definition: gc.h:53
VALUE rb_fiber_new(VALUE(*func)(ANYARGS), VALUE obj)
Definition: cont.c:1223
int argc
Definition: ruby.c:131
#define Qfalse
Definition: ruby.h:425
void ruby_Init_Fiber_as_Coroutine(void)
Definition: cont.c:1689
void rb_fiber_start(void)
Definition: cont.c:1270
#define ALLOCA_N(type, n)
Definition: ruby.h:1337
static void cont_mark(void *ptr)
Definition: cont.c:174
static VALUE rb_callcc(VALUE self)
Definition: cont.c:918
#define MEMCPY(p1, p2, type, n)
Definition: ruby.h:1352
#define rb_ary_new4
Definition: intern.h:92
static VALUE rb_fiber_m_transfer(int argc, VALUE *argv, VALUE fibval)
Definition: cont.c:1598
int err
Definition: win32.c:114
static VALUE rb_fiber_s_current(VALUE klass)
Definition: cont.c:1631
#define ALLOC(type)
Definition: ruby.h:1334
Definition: cont.c:118
size_t vm_stack_slen
Definition: cont.c:97
VALUE rb_yield(VALUE)
Definition: vm_eval.c:942
#define RARRAY_CONST_PTR(a)
Definition: ruby.h:886
#define REALLOC_N(var, type, n)
Definition: ruby.h:1335
SSL_METHOD *(* func)(void)
Definition: ossl_ssl.c:113
int errno
#define STACK_DIR_UPPER(a, b)
Definition: gc.h:82
void rb_vm_stack_to_heap(rb_thread_t *th)
Definition: vm.c:635
RUBY_SYMBOL_EXPORT_BEGIN void ruby_Init_Continuation_body(void)
Definition: cont.c:1678
static rb_context_t * cont_new(VALUE klass)
Definition: cont.c:454
#define RUBY_SYMBOL_EXPORT_END
Definition: missing.h:39
void ruby_xfree(void *x)
Definition: gc.c:6242
static VALUE rb_cont_call(int argc, VALUE *argv, VALUE contval)
Definition: cont.c:1035
void Init_Cont(void)
Definition: cont.c:1652
unsigned char buf[MIME_BUF_SIZE]
Definition: nkf.c:4308
static VALUE rb_cFiber
Definition: cont.c:156
#define Qnil
Definition: ruby.h:427
unsigned long VALUE
Definition: ruby.h:88
#define STACK_GROW_DIR_DETECTION
Definition: gc.h:81
rb_context_t cont
Definition: cont.c:134
RUBY_JMP_BUF rb_jmpbuf_t
Definition: vm_core.h:482
static VALUE fiber_store(rb_fiber_t *next_fib)
Definition: cont.c:1344
struct rb_fiber_struct rb_fiber_t
VALUE first_proc
Definition: vm_core.h:615
#define RUBY_SYMBOL_EXPORT_BEGIN
Definition: missing.h:38
static void fiber_mark(void *ptr)
Definition: cont.c:304
void rb_thread_mark(void *th)
Definition: vm.c:1979
#define TH_PUSH_TAG(th)
Definition: eval_intern.h:122
#define SET_MACHINE_STACK_END(p)
Definition: gc.h:11
struct rb_ensure_list * next
Definition: vm_core.h:520
st_table * st_init_numtable(void)
Definition: st.c:272
static void cont_init(rb_context_t *cont, rb_thread_t *th)
Definition: cont.c:446
static VALUE rb_fiber_s_yield(int argc, VALUE *argv, VALUE klass)
Definition: cont.c:1617
#define FIBER_USE_NATIVE
Definition: cont.c:68
#define ruby_setjmp(env)
Definition: eval_intern.h:50
enum rb_thread_status status
Definition: vm_core.h:562
#define RUBY_FREE_UNLESS_NULL(ptr)
Definition: gc.h:61
struct rb_fiber_struct * next_fiber
Definition: cont.c:138
VALUE * stack_src
Definition: cont.c:102
static void cont_restore_thread(rb_context_t *cont)
Definition: cont.c:526
int size
Definition: encoding.c:49
#define f
int mark_stack_len
Definition: vm_core.h:631
struct rb_vm_struct::@168 default_params
VALUE root_svar
Definition: vm_core.h:558
rb_block_t block
Definition: vm_core.h:701
VALUE * stack_start
Definition: vm_core.h:621
VALUE rb_block_proc(void)
Definition: proc.c:641
size_t st_memsize(const st_table *)
Definition: st.c:342
#define ANYARGS
Definition: defines.h:98
const rb_method_entry_t * me
Definition: vm_core.h:455
#define RUBY_FREE_LEAVE(msg)
Definition: gc.h:56
VALUE marker
Definition: vm_core.h:514
#define RUBY_FREE_ENTER(msg)
Definition: gc.h:55
RUBY_EXTERN char * strerror(int)
Definition: strerror.c:11
VALUE rb_obj_is_fiber(VALUE obj)
Definition: cont.c:370
#define STACK_PAD_SIZE
VALUE rb_fiber_transfer(VALUE fib, int argc, VALUE *argv)
Definition: cont.c:1474
VALUE rb_proc_new(VALUE(*)(ANYARGS), VALUE)
Definition: proc.c:2321
VALUE root_fiber
Definition: vm_core.h:642
static rb_fiber_t * fiber_t_alloc(VALUE fibval)
Definition: cont.c:1146
#define TypedData_Make_Struct(klass, type, data_type, sval)
Definition: ruby.h:1030
struct rb_fiber_struct * prev_fiber
Definition: cont.c:137
#define GetThreadPtr(obj, ptr)
Definition: vm_core.h:472
rb_ensure_list_t * ensure_list
Definition: vm_core.h:646
rb_jmpbuf_t root_jmpbuf
Definition: vm_core.h:643
static void cont_restore_0(rb_context_t *cont, VALUE *addr_in_prev_frame)
Definition: cont.c:801
VALUE(* e_proc)(ANYARGS)
Definition: vm_core.h:515
size_t stack_size
Definition: vm_core.h:530
static void fiber_link_remove(rb_fiber_t *fib)
Definition: cont.c:330
struct rb_vm_tag * tag
Definition: vm_core.h:593
void ruby_register_rollback_func_for_ensure(VALUE(*ensure_func)(ANYARGS), VALUE(*rollback_func)(ANYARGS))
Definition: cont.c:947
context_type
Definition: cont.c:84
size_t stack_size
Definition: cont.c:103
static void fiber_free(void *ptr)
Definition: cont.c:337
VALUE rb_vm_make_jump_tag_but_local_jump(int state, VALUE val)
Definition: vm.c:1066
static size_t cont_memsize(const void *ptr)
Definition: cont.c:276
size_t stack_maxsize
Definition: vm_core.h:623
#define stat(path, st)
Definition: win32.h:213
static void cont_save_machine_stack(rb_thread_t *th, rb_context_t *cont)
Definition: cont.c:381
fiber_status
Definition: cont.c:116
NORETURN(NOINLINE(static void cont_restore_0(rb_context_t *, VALUE *)))
static void rollback_ensure_stack(VALUE self, rb_ensure_list_t *current, rb_ensure_entry_t *target)
Definition: cont.c:968
#define NULL
Definition: _sdbm.c:103
#define Qundef
Definition: ruby.h:428
static rb_thread_t * GET_THREAD(void)
Definition: vm_core.h:924
void rb_define_method(VALUE klass, const char *name, VALUE(*func)(ANYARGS), int argc)
Definition: class.c:1488
struct rb_trace_arg_struct * trace_arg
Definition: vm_core.h:638
char ** argv
Definition: ruby.c:132
VALUE * ep
Definition: vm_core.h:465
rb_ensure_list_t * ensure_list
Definition: cont.c:113
NOINLINE(static VALUE cont_capture(volatile int *stat))
#define GET_VM()
Definition: vm_core.h:917