1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13: #include "ruby/ruby.h"
14: #include "vm_core.h"
15: #include "gc.h"
16: #include "eval_intern.h"
17:
18: enum context_type {
19: CONTINUATION_CONTEXT = 0,
20: FIBER_CONTEXT = 1,
21: ROOT_FIBER_CONTEXT = 2,
22: };
23:
24: typedef struct rb_context_struct {
25: VALUE self;
26: VALUE value;
27: VALUE *vm_stack;
28: VALUE *machine_stack;
29: VALUE *machine_stack_src;
30: #ifdef __ia64
31: VALUE *machine_register_stack;
32: VALUE *machine_register_stack_src;
33: int machine_register_stack_size;
34: #endif
35: rb_thread_t saved_thread;
36: rb_jmpbuf_t jmpbuf;
37: int machine_stack_size;
38: VALUE prev;
39: int alive;
40: enum context_type type;
41: } rb_context_t;
42:
43: static VALUE rb_cContinuation;
44: static VALUE rb_cFiber;
45: static VALUE rb_eFiberError;
46:
47: #define GetContPtr(obj, ptr) \
48: Data_Get_Struct(obj, rb_context_t, ptr)
49:
50: NOINLINE(static VALUE cont_capture(volatile int *stat));
51:
52: void rb_thread_mark(rb_thread_t *th);
53:
54: static void
55: cont_mark(void *ptr)
56: {
57: RUBY_MARK_ENTER("cont");
58: if (ptr) {
59: rb_context_t *cont = ptr;
60: rb_gc_mark(cont->value);
61: rb_gc_mark(cont->prev);
62: rb_thread_mark(&cont->saved_thread);
63:
64: if (cont->vm_stack) {
65: rb_gc_mark_locations(cont->vm_stack,
66: cont->vm_stack + cont->saved_thread.stack_size);
67: }
68:
69: if (cont->machine_stack) {
70: rb_gc_mark_locations(cont->machine_stack,
71: cont->machine_stack + cont->machine_stack_size);
72: }
73: #ifdef __ia64
74: if (cont->machine_register_stack) {
75: rb_gc_mark_locations(cont->machine_register_stack,
76: cont->machine_register_stack + cont->machine_register_stack_size);
77: }
78: #endif
79: }
80: RUBY_MARK_LEAVE("cont");
81: }
82:
83: static void
84: cont_free(void *ptr)
85: {
86: RUBY_FREE_ENTER("cont");
87: if (ptr) {
88: rb_context_t *cont = ptr;
89: RUBY_FREE_UNLESS_NULL(cont->saved_thread.stack);
90: RUBY_FREE_UNLESS_NULL(cont->machine_stack);
91: #ifdef __ia64
92: RUBY_FREE_UNLESS_NULL(cont->machine_register_stack);
93: #endif
94: RUBY_FREE_UNLESS_NULL(cont->vm_stack);
95:
96: if (cont->type == FIBER_CONTEXT) {
97: st_free_table(cont->saved_thread.local_storage);
98: }
99:
100: ruby_xfree(ptr);
101: }
102: RUBY_FREE_LEAVE("cont");
103: }
104:
105: static void
106: cont_save_machine_stack(rb_thread_t *th, rb_context_t *cont)
107: {
108: int size;
109: rb_thread_t *sth = &cont->saved_thread;
110:
111: SET_MACHINE_STACK_END(&th->machine_stack_end);
112: #ifdef __ia64
113: th->machine_register_stack_end = rb_ia64_bsp();
114: #endif
115:
116: if (th->machine_stack_start > th->machine_stack_end) {
117: size = cont->machine_stack_size = th->machine_stack_start - th->machine_stack_end;
118: cont->machine_stack_src = th->machine_stack_end;
119: }
120: else {
121: size = cont->machine_stack_size = th->machine_stack_end - th->machine_stack_start;
122: cont->machine_stack_src = th->machine_stack_start;
123: }
124:
125: if (cont->machine_stack) {
126: REALLOC_N(cont->machine_stack, VALUE, size);
127: }
128: else {
129: cont->machine_stack = ALLOC_N(VALUE, size);
130: }
131:
132: FLUSH_REGISTER_WINDOWS;
133: MEMCPY(cont->machine_stack, cont->machine_stack_src, VALUE, size);
134:
135: #ifdef __ia64
136: rb_ia64_flushrs();
137: size = cont->machine_register_stack_size = th->machine_register_stack_end - th->machine_register_stack_start;
138: cont->machine_register_stack_src = th->machine_register_stack_start;
139: if (cont->machine_register_stack) {
140: REALLOC_N(cont->machine_register_stack, VALUE, size);
141: }
142: else {
143: cont->machine_register_stack = ALLOC_N(VALUE, size);
144: }
145:
146: MEMCPY(cont->machine_register_stack, cont->machine_register_stack_src, VALUE, size);
147: #endif
148:
149: sth->machine_stack_start = sth->machine_stack_end = 0;
150: #ifdef __ia64
151: sth->machine_register_stack_start = sth->machine_register_stack_end = 0;
152: #endif
153: }
154:
155: static rb_context_t *
156: cont_new(VALUE klass)
157: {
158: rb_context_t *cont;
159: volatile VALUE contval;
160: rb_thread_t *th = GET_THREAD();
161:
162: contval = Data_Make_Struct(klass, rb_context_t,
163: cont_mark, cont_free, cont);
164:
165: cont->self = contval;
166: cont->alive = Qtrue;
167:
168:
169: cont->saved_thread = *th;
170:
171: return cont;
172: }
173:
174: void vm_stack_to_heap(rb_thread_t *th);
175:
176: static VALUE
177: cont_capture(volatile int *stat)
178: {
179: rb_context_t *cont;
180: rb_thread_t *th = GET_THREAD(), *sth;
181: volatile VALUE contval;
182:
183: vm_stack_to_heap(th);
184: cont = cont_new(rb_cContinuation);
185: contval = cont->self;
186: sth = &cont->saved_thread;
187:
188: cont->vm_stack = ALLOC_N(VALUE, th->stack_size);
189: MEMCPY(cont->vm_stack, th->stack, VALUE, th->stack_size);
190: sth->stack = 0;
191:
192: cont_save_machine_stack(th, cont);
193:
194: if (ruby_setjmp(cont->jmpbuf)) {
195: VALUE value;
196:
197: value = cont->value;
198: cont->value = Qnil;
199: *stat = 1;
200: return value;
201: }
202: else {
203: *stat = 0;
204: return cont->self;
205: }
206: }
207:
208: NORETURN(static void cont_restore_1(rb_context_t *));
209:
210: static void
211: cont_restore_1(rb_context_t *cont)
212: {
213: rb_thread_t *th = GET_THREAD(), *sth = &cont->saved_thread;
214:
215:
216: if (cont->type == CONTINUATION_CONTEXT) {
217:
218: VALUE fib;
219:
220: th->fiber = sth->fiber;
221: fib = th->fiber ? th->fiber : th->root_fiber;
222:
223: if (fib) {
224: rb_context_t *fcont;
225: GetContPtr(fib, fcont);
226: th->stack_size = fcont->saved_thread.stack_size;
227: th->stack = fcont->saved_thread.stack;
228: }
229: MEMCPY(th->stack, cont->vm_stack, VALUE, sth->stack_size);
230: }
231: else {
232:
233: th->stack = sth->stack;
234: th->stack_size = sth->stack_size;
235: th->local_storage = sth->local_storage;
236: th->fiber = cont->self;
237: }
238:
239: th->cfp = sth->cfp;
240: th->safe_level = sth->safe_level;
241: th->raised_flag = sth->raised_flag;
242: th->state = sth->state;
243: th->status = sth->status;
244: th->tag = sth->tag;
245: th->trap_tag = sth->trap_tag;
246: th->errinfo = sth->errinfo;
247: th->first_proc = sth->first_proc;
248:
249:
250: #ifdef _M_AMD64
251: {
252:
253: jmp_buf buf;
254: setjmp(buf);
255: ((_JUMP_BUFFER*)(&cont->jmpbuf))->Frame =
256: ((_JUMP_BUFFER*)(&buf))->Frame;
257: }
258: #endif
259: if (cont->machine_stack_src) {
260: FLUSH_REGISTER_WINDOWS;
261: MEMCPY(cont->machine_stack_src, cont->machine_stack,
262: VALUE, cont->machine_stack_size);
263: }
264:
265: #ifdef __ia64
266: if (cont->machine_register_stack_src) {
267: MEMCPY(cont->machine_register_stack_src, cont->machine_register_stack,
268: VALUE, cont->machine_register_stack_size);
269: }
270: #endif
271:
272: ruby_longjmp(cont->jmpbuf, 1);
273: }
274:
275: NORETURN(NOINLINE(static void cont_restore_0(rb_context_t *, VALUE *)));
276:
277: #ifdef __ia64
278: #define C(a) rse_##a##0, rse_##a##1, rse_##a##2, rse_##a##3, rse_##a##4
279: #define E(a) rse_##a##0= rse_##a##1= rse_##a##2= rse_##a##3= rse_##a##4
280: static volatile int C(a), C(b), C(c), C(d), C(e);
281: static volatile int C(f), C(g), C(h), C(i), C(j);
282: static volatile int C(k), C(l), C(m), C(n), C(o);
283: static volatile int C(p), C(q), C(r), C(s), C(t);
284: int rb_dummy_false = 0;
285: NORETURN(NOINLINE(static void register_stack_extend(rb_context_t *, VALUE *)));
286: static void
287: register_stack_extend(rb_context_t *cont, VALUE *curr_bsp)
288: {
289: if (rb_dummy_false) {
290:
291: E(a) = E(b) = E(c) = E(d) = E(e) =
292: E(f) = E(g) = E(h) = E(i) = E(j) =
293: E(k) = E(l) = E(m) = E(n) = E(o) =
294: E(p) = E(q) = E(r) = E(s) = E(t) = 0;
295: E(a) = E(b) = E(c) = E(d) = E(e) =
296: E(f) = E(g) = E(h) = E(i) = E(j) =
297: E(k) = E(l) = E(m) = E(n) = E(o) =
298: E(p) = E(q) = E(r) = E(s) = E(t) = 0;
299: }
300: if (curr_bsp < cont->machine_register_stack_src+cont->machine_register_stack_size) {
301: register_stack_extend(cont, (VALUE*)rb_ia64_bsp());
302: }
303: cont_restore_1(cont);
304: }
305: #undef C
306: #undef E
307: #endif
308:
309: static void
310: cont_restore_0(rb_context_t *cont, VALUE *addr_in_prev_frame)
311: {
312: if (cont->machine_stack_src) {
313: #define STACK_PAD_SIZE 1024
314: VALUE space[STACK_PAD_SIZE];
315:
316: #if STACK_GROW_DIRECTION < 0
317: if (addr_in_prev_frame > cont->machine_stack_src) {
318: cont_restore_0(cont, &space[0]);
319: }
320: #elif STACK_GROW_DIRECTION > 0
321: if (addr_in_prev_frame < cont->machine_stack_src + cont->machine_stack_size) {
322: cont_restore_0(cont, &space[STACK_PAD_SIZE-1]);
323: }
324: #else
325: if (addr_in_prev_frame > &space[0]) {
326:
327: if (addr_in_prev_frame > cont->saved_thread.machine_stack_src) {
328: cont_restore_0(cont, &space[0]);
329: }
330: }
331: else {
332:
333: if (addr_in_prev_frame < cont->machine_stack_src + cont->machine_stack_size) {
334: cont_restore_0(cont, &space[STACK_PAD_SIZE-1]);
335: }
336: }
337: #endif
338: }
339: #ifdef __ia64
340: register_stack_extend(cont, (VALUE*)rb_ia64_bsp());
341: #else
342: cont_restore_1(cont);
343: #endif
344: }
345:
346:
347:
348:
349:
350:
351:
352:
353:
354:
355:
356:
357:
358:
359:
360:
361:
362:
363:
364:
365:
366:
367:
368:
369:
370:
371:
372:
373:
374:
375:
376:
377:
378:
379:
380:
381:
382:
383:
384:
385:
386:
387:
388:
389:
390:
391:
392:
393:
394:
395:
396:
397:
398:
399:
400:
401:
402:
403:
404:
405:
406:
407: static VALUE
408: rb_callcc(VALUE self)
409: {
410: volatile int called;
411: volatile VALUE val = cont_capture(&called);
412:
413: if (called) {
414: return val;
415: }
416: else {
417: return rb_yield(val);
418: }
419: }
420:
421: static VALUE
422: make_passing_arg(int argc, VALUE *argv)
423: {
424: switch(argc) {
425: case 0:
426: return Qnil;
427: case 1:
428: return argv[0];
429: default:
430: return rb_ary_new4(argc, argv);
431: }
432: }
433:
434:
435:
436:
437:
438:
439:
440:
441:
442:
443:
444:
445:
446:
447:
448:
449:
450: static VALUE
451: rb_cont_call(int argc, VALUE *argv, VALUE contval)
452: {
453: rb_context_t *cont;
454: rb_thread_t *th = GET_THREAD();
455: GetContPtr(contval, cont);
456:
457: if (cont->saved_thread.self != th->self) {
458: rb_raise(rb_eRuntimeError, "continuation called across threads");
459: }
460: if (cont->saved_thread.trap_tag != th->trap_tag) {
461: rb_raise(rb_eRuntimeError, "continuation called across trap");
462: }
463: if (cont->saved_thread.fiber) {
464: rb_context_t *fcont;
465: GetContPtr(cont->saved_thread.fiber, fcont);
466:
467: if (th->fiber != cont->saved_thread.fiber) {
468: rb_raise(rb_eRuntimeError, "continuation called across fiber");
469: }
470:
471: if (!fcont->alive) {
472: rb_raise(rb_eRuntimeError, "continuation called dead fiber");
473: }
474: }
475:
476: cont->value = make_passing_arg(argc, argv);
477:
478: cont_restore_0(cont, &contval);
479: return Qnil;
480: }
481:
482:
483:
484:
485:
486: #define FIBER_VM_STACK_SIZE (4 * 1024)
487:
488: static rb_context_t *
489: fiber_alloc(VALUE klass)
490: {
491: rb_context_t *cont = cont_new(klass);
492:
493: cont->type = FIBER_CONTEXT;
494: cont->prev = Qnil;
495:
496: return cont;
497: }
498:
499: static VALUE
500: fiber_new(VALUE klass, VALUE proc)
501: {
502: rb_context_t *cont = fiber_alloc(klass);
503: VALUE contval = cont->self;
504: rb_thread_t *th = &cont->saved_thread;
505:
506:
507: cont->vm_stack = 0;
508:
509: th->stack = 0;
510: th->stack_size = FIBER_VM_STACK_SIZE;
511: th->stack = ALLOC_N(VALUE, th->stack_size);
512:
513: th->cfp = (void *)(th->stack + th->stack_size);
514: th->cfp--;
515: th->cfp->pc = 0;
516: th->cfp->sp = th->stack + 1;
517: th->cfp->bp = 0;
518: th->cfp->lfp = th->stack;
519: *th->cfp->lfp = 0;
520: th->cfp->dfp = th->stack;
521: th->cfp->self = Qnil;
522: th->cfp->flag = 0;
523: th->cfp->iseq = 0;
524: th->cfp->proc = 0;
525: th->cfp->block_iseq = 0;
526: th->tag = 0;
527: th->local_storage = st_init_numtable();
528:
529: th->first_proc = proc;
530:
531: MEMCPY(&cont->jmpbuf, &th->root_jmpbuf, rb_jmpbuf_t, 1);
532:
533: return contval;
534: }
535:
536: VALUE
537: rb_fiber_new(VALUE (*func)(ANYARGS), VALUE obj)
538: {
539: return fiber_new(rb_cFiber, rb_proc_new(func, obj));
540: }
541:
542: static VALUE
543: rb_fiber_s_new(VALUE self)
544: {
545: return fiber_new(self, rb_block_proc());
546: }
547:
548: static VALUE
549: return_fiber(void)
550: {
551: rb_context_t *cont;
552: VALUE curr = rb_fiber_current();
553: GetContPtr(curr, cont);
554:
555: if (cont->prev == Qnil) {
556: rb_thread_t *th = GET_THREAD();
557:
558: if (th->root_fiber != curr) {
559: return th->root_fiber;
560: }
561: else {
562: rb_raise(rb_eFiberError, "can't yield from root fiber");
563: }
564: }
565: else {
566: VALUE prev = cont->prev;
567: cont->prev = Qnil;
568: return prev;
569: }
570: }
571:
572: VALUE rb_fiber_transfer(VALUE fib, int argc, VALUE *argv);
573:
574: static void
575: rb_fiber_terminate(rb_context_t *cont)
576: {
577: VALUE value = cont->value;
578: cont->alive = Qfalse;
579: rb_fiber_transfer(return_fiber(), 1, &value);
580: }
581:
582: void
583: rb_fiber_start(void)
584: {
585: rb_thread_t *th = GET_THREAD();
586: rb_context_t *cont;
587: rb_proc_t *proc;
588: VALUE args;
589: int state;
590:
591: GetContPtr(th->fiber, cont);
592: TH_PUSH_TAG(th);
593: if ((state = EXEC_TAG()) == 0) {
594: GetProcPtr(cont->saved_thread.first_proc, proc);
595: args = cont->value;
596: cont->value = Qnil;
597: th->errinfo = Qnil;
598: th->local_lfp = proc->block.lfp;
599: th->local_svar = Qnil;
600:
601: cont->value = vm_invoke_proc(th, proc, proc->block.self, 1, &args, 0);
602: }
603: