Skip to content

HTTPS clone URL

Subversion checkout URL

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