67
77
FREE_REPORT_LEAVE("cont");
71
cont_capture(volatile int *stat)
81
cont_save_machine_stack(rb_thread_t *th, rb_context_t *cont)
73
rb_continuation_t *cont;
75
rb_thread_t *th = GET_THREAD(), *sth;
78
contval = Data_Make_Struct(rb_cCont, rb_continuation_t,
79
cont_mark, cont_free, cont);
82
cont->saved_thread = *th;
83
sth = &cont->saved_thread;
84
sth->stack = 0; /* clear to skip GC marking */
85
cont->vm_stack = ALLOC_N(VALUE, sth->stack_size);
86
MEMCPY(cont->vm_stack, th->stack, VALUE, sth->stack_size);
88
85
rb_gc_set_stack_end(&th->machine_stack_end);
89
86
if (th->machine_stack_start > th->machine_stack_end) {
90
87
size = cont->machine_stack_size = th->machine_stack_start - th->machine_stack_end;
95
92
cont->machine_stack_src = th->machine_stack_start;
98
cont->machine_stack = ALLOC_N(VALUE, size);
95
if (cont->machine_stack) {
96
REALLOC_N(cont->machine_stack, VALUE, size);
99
cont->machine_stack = ALLOC_N(VALUE, size);
99
102
MEMCPY(cont->machine_stack, cont->machine_stack_src, VALUE, size);
105
static rb_context_t *
106
cont_new(VALUE klass)
109
volatile VALUE contval;
110
rb_thread_t *th = GET_THREAD(), *sth;
112
contval = Data_Make_Struct(klass, rb_context_t,
113
cont_mark, cont_free, cont);
114
cont->self = contval;
118
cont->saved_thread = *th;
119
sth = &cont->saved_thread;
124
void th_stack_to_heap(rb_thread_t *th);
127
cont_capture(volatile int *stat)
132
th_stack_to_heap(GET_THREAD());
133
cont = cont_new(rb_cCont);
134
th = &cont->saved_thread;
136
cont->vm_stack = ALLOC_N(VALUE, th->stack_size);
137
MEMCPY(cont->vm_stack, th->stack, VALUE, th->stack_size);
140
cont_save_machine_stack(th, cont);
101
142
if (ruby_setjmp(cont->jmpbuf)) {
104
retval = cont->retval;
156
NORETURN(static void cont_restore_1(rb_context_t *));
116
cont_restore_context_1(rb_continuation_t *cont)
159
cont_restore_1(rb_context_t *cont)
118
161
rb_thread_t *th = GET_THREAD(), *sth = &cont->saved_thread;
120
163
/* restore thread context */
121
MEMCPY(th->stack, cont->vm_stack, VALUE, sth->stack_size);
166
th->stack = sth->stack;
167
th->stack_size = sth->stack_size;
168
th->fiber = cont->self;
172
MEMCPY(th->stack, cont->vm_stack, VALUE, sth->stack_size);
173
th->fiber = sth->fiber;
123
176
th->cfp = sth->cfp;
124
177
th->safe_level = sth->safe_level;
126
179
th->state = sth->state;
127
180
th->status = sth->status;
128
181
th->tag = sth->tag;
182
th->trap_tag = sth->trap_tag;
183
th->errinfo = sth->errinfo;
184
th->first_proc = sth->first_proc;
130
186
/* restore machine stack */
131
MEMCPY(cont->machine_stack_src, cont->machine_stack,
132
VALUE, cont->machine_stack_size);
187
if (cont->machine_stack_src) {
188
MEMCPY(cont->machine_stack_src, cont->machine_stack,
189
VALUE, cont->machine_stack_size);
134
192
ruby_longjmp(cont->jmpbuf, 1);
137
NORETURN(NOINLINE(static void restore_context_0(rb_continuation_t *, VALUE *)));
195
NORETURN(NOINLINE(static void cont_restore_0(rb_context_t *, VALUE *)));
140
cont_restore_context_0(rb_continuation_t *cont, VALUE *addr_in_prev_frame)
198
cont_restore_0(rb_context_t *cont, VALUE *addr_in_prev_frame)
200
if (cont->machine_stack_src) {
142
201
#define STACK_PAD_SIZE 1024
143
VALUE space[STACK_PAD_SIZE];
202
VALUE space[STACK_PAD_SIZE];
145
204
#if STACK_GROW_DIRECTION < 0 /* downward */
146
if (addr_in_prev_frame > cont->machine_stack_src) {
147
cont_restore_context_0(cont, &space[0]);
205
if (addr_in_prev_frame > cont->machine_stack_src) {
206
cont_restore_0(cont, &space[0]);
149
208
#elif STACK_GROW_DIRECTION > 0 /* upward */
150
if (addr_in_prev_frame < cont->machine_stack_src + cont->machine_stack_size) {
151
cont_restore_context_0(cont, &space[STACK_PAD_SIZE-1]);
209
if (addr_in_prev_frame < cont->machine_stack_src + cont->machine_stack_size) {
210
cont_restore_0(cont, &space[STACK_PAD_SIZE-1]);
154
if (addr_in_prev_frame > &space[0]) {
155
/* Stack grows downward */
156
if (addr_in_prev_frame > cont->saved_thread.machine_stack_src) {
157
cont_restore_context_0(cont, &space[0]);
161
/* Stack grows upward */
162
if (addr_in_prev_frame < cont->machine_stack_src + cont->machine_stack_size) {
163
cont_restore_context_0(cont, &space[STACK_PAD_SIZE-1]);
213
if (addr_in_prev_frame > &space[0]) {
214
/* Stack grows downward */
215
if (addr_in_prev_frame > cont->saved_thread.machine_stack_src) {
216
cont_restore_0(cont, &space[0]);
220
/* Stack grows upward */
221
if (addr_in_prev_frame < cont->machine_stack_src + cont->machine_stack_size) {
222
cont_restore_0(cont, &space[STACK_PAD_SIZE-1]);
167
cont_restore_context_1(cont);
227
cont_restore_1(cont);
264
335
rb_cont_call(int argc, VALUE *argv, VALUE contval)
266
rb_continuation_t *cont;
267
338
rb_thread_t *th = GET_THREAD();
268
339
GetContPtr(contval, cont);
270
if (cont->saved_thread.value != th->value) {
341
if (cont->saved_thread.self != th->self) {
271
342
rb_raise(rb_eRuntimeError, "continuation called across threads");
273
344
if (cont->saved_thread.trap_tag != th->trap_tag) {
274
345
rb_raise(rb_eRuntimeError, "continuation called across trap");
347
if (cont->saved_thread.fiber) {
349
GetContPtr(cont->saved_thread.fiber, fcont);
282
cont->retval = argv[0];
285
cont->retval = rb_ary_new4(argc, argv);
352
rb_raise(rb_eRuntimeError, "continuation called dead fiber");
289
cont_restore_context_0(cont, (VALUE *)&cont);
356
cont->value = make_passing_arg(argc, argv);
358
cont_restore_0(cont, (VALUE *)&cont);
290
359
return Qnil; /* unreachable */
366
#define FIBER_STACK_SIZE (4 * 1024)
369
rb_fiber_s_new(VALUE self)
371
rb_context_t *cont = cont_new(self);
372
rb_thread_t *th = &cont->saved_thread;
379
th->stack_size = FIBER_STACK_SIZE;
380
th->stack = ALLOC_N(VALUE, th->stack_size);
381
th->cfp = (void *)(th->stack + th->stack_size);
384
th->cfp->sp = th->stack + 1;
386
th->cfp->lfp = th->stack;
388
th->cfp->dfp = th->stack;
389
th->cfp->self = Qnil;
393
th->cfp->block_iseq = 0;
395
th->first_proc = rb_block_proc();
397
MEMCPY(&cont->jmpbuf, &th->root_jmpbuf, rb_jmpbuf_t, 1);
402
static VALUE rb_fiber_yield(int argc, VALUE *args, VALUE fval);
405
rb_fiber_terminate(rb_context_t *cont)
407
rb_context_t *prev_cont;
408
VALUE value = cont->value;
410
GetContPtr(cont->prev, prev_cont);
412
cont->alive = Qfalse;
415
if (prev_cont->alive == Qfalse) {
416
rb_fiber_yield(1, &value, GET_THREAD()->root_fiber);
419
rb_fiber_yield(1, &value, cont->prev);
426
rb_thread_t *th = GET_THREAD();
435
if ((state = EXEC_TAG()) == 0) {
436
GetContPtr(th->fiber, cont);
437
GetProcPtr(cont->saved_thread.first_proc, proc);
441
th->local_lfp = proc->block.lfp;
442
th->local_svar = Qnil;
444
cont->value = th_invoke_proc(th, proc, proc->block.self, 1, &args);
449
th->thrown_errinfo = th_make_jump_tag_but_local_jump(state, th->errinfo);
450
th->interrupt_flag = 1;
453
rb_fiber_terminate(cont);
454
rb_bug("rb_fiber_start: unreachable");
458
rb_fiber_current(rb_thread_t *th)
460
if (th->fiber == 0) {
462
th->root_fiber = th->fiber = cont_new(rb_cFiber)->self;
468
cont_store(rb_context_t *next_cont)
470
rb_thread_t *th = GET_THREAD();
474
GetContPtr(th->fiber, cont);
475
cont->saved_thread = *th;
478
/* create current fiber */
479
cont = cont_new(rb_cFiber); /* no need to allocate vm stack */
480
th->root_fiber = th->fiber = cont->self;
484
next_cont->prev = cont->self;
486
cont_save_machine_stack(th, cont);
488
if (ruby_setjmp(cont->jmpbuf)) {
490
GetContPtr(th->fiber, cont);
499
rb_fiber_yield(int argc, VALUE *argv, VALUE fval)
503
rb_thread_t *th = GET_THREAD();
505
GetContPtr(fval, cont);
507
if (cont->saved_thread.self != th->self) {
508
rb_raise(rb_eFiberError, "fiber called across threads");
510
if (cont->saved_thread.trap_tag != th->trap_tag) {
511
rb_raise(rb_eFiberError, "fiber called across trap");
514
rb_raise(rb_eFiberError, "dead fiber called");
517
cont->value = make_passing_arg(argc, argv);
519
if ((value = cont_store(cont)) == Qundef) {
520
cont_restore_0(cont, (VALUE *)&cont);
521
rb_bug("rb_fiber_yield: unreachable");
528
rb_fiber_prev(VALUE fval)
531
GetContPtr(fval, cont);
536
rb_fiber_alive_p(VALUE fval)
539
GetContPtr(fval, cont);
544
rb_fiber_s_current(VALUE klass)
546
return rb_fiber_current(GET_THREAD());
550
rb_fiber_s_prev(VALUE klass)
552
return rb_fiber_prev(rb_fiber_s_current(Qnil));
556
rb_fiber_s_yield(int argc, VALUE *argv, VALUE fval)
558
return rb_fiber_yield(argc, argv, rb_fiber_s_prev(Qnil));
299
567
rb_define_method(rb_cCont, "call", rb_cont_call, -1);
300
568
rb_define_method(rb_cCont, "[]", rb_cont_call, -1);
301
569
rb_define_global_function("callcc", rb_callcc, 0);
571
rb_cFiber = rb_define_class("Fiber", rb_cObject);
572
rb_undef_alloc_func(rb_cFiber);
573
rb_define_method(rb_cFiber, "yield", rb_fiber_yield, -1);
574
rb_define_method(rb_cFiber, "prev", rb_fiber_prev, 0);
575
rb_define_method(rb_cFiber, "alive?", rb_fiber_alive_p, 0);
577
rb_define_singleton_method(rb_cFiber, "current", rb_fiber_s_current, 0);
578
rb_define_singleton_method(rb_cFiber, "prev", rb_fiber_s_prev, 0);
579
rb_define_singleton_method(rb_cFiber, "yield", rb_fiber_s_yield, -1);
580
rb_define_singleton_method(rb_cFiber, "new", rb_fiber_s_new, 0);
582
rb_eFiberError = rb_define_class("FiberError", rb_eStandardError);