~ubuntu-branches/ubuntu/trusty/ruby1.9/trusty

« back to all changes in this revision

Viewing changes to cont.c

  • Committer: Bazaar Package Importer
  • Author(s): akira yamada
  • Date: 2007-06-06 11:58:24 UTC
  • mfrom: (1.1.7 upstream)
  • Revision ID: james.westby@ubuntu.com-20070606115824-qzldkdwq3dvfpf84
Tags: 1.9.0+20070606-1
* new upstream snapshot. (2006-06-06)
* updated debian/generated-incs/* files.

Show diffs side-by-side

added added

removed removed

Lines of Context:
3
3
  cont.c - 
4
4
 
5
5
  $Author: ko1 $
6
 
  $Date: 2007-05-24 12:08:13 +0900 (木, 24  5月 2007) $
 
6
  $Date: 2007-06-06 10:55:09 +0900 (水, 06  6月 2007) $
7
7
  created at: Thu May 23 09:03:43 2007
8
8
 
9
9
  Copyright (C) 2007 Koichi Sasada
15
15
#include "gc.h"
16
16
#include "eval_intern.h"
17
17
 
18
 
typedef struct rb_continuation_struct {
19
 
    rb_thread_t saved_thread;
20
 
    rb_jmpbuf_t jmpbuf;
21
 
    VALUE retval;
 
18
typedef struct rb_context_struct {
 
19
    VALUE self;
 
20
    VALUE value;
 
21
    VALUE prev; /* for fiber */
22
22
    VALUE *vm_stack;
23
23
    VALUE *machine_stack;
24
24
    VALUE *machine_stack_src;
 
25
    rb_thread_t saved_thread;
 
26
    rb_jmpbuf_t jmpbuf;
25
27
    int machine_stack_size;
26
 
} rb_continuation_t;
 
28
    int alive;
 
29
} rb_context_t;
 
30
 
 
31
VALUE rb_cCont;
 
32
VALUE rb_cFiber;
 
33
VALUE rb_eFiberError;
27
34
 
28
35
#define GetContPtr(obj, ptr)  \
29
 
  Data_Get_Struct(obj, rb_continuation_t, ptr)
 
36
  Data_Get_Struct(obj, rb_context_t, ptr)
30
37
 
31
38
NOINLINE(static VALUE cont_capture(volatile int *stat));
32
39
 
37
44
{
38
45
    MARK_REPORT_ENTER("cont");
39
46
    if (ptr) {
40
 
        rb_continuation_t *cont = ptr;
41
 
        rb_gc_mark(cont->retval);
 
47
        rb_context_t *cont = ptr;
 
48
        rb_gc_mark(cont->value);
 
49
        rb_gc_mark(cont->prev);
 
50
 
42
51
        rb_thread_mark(&cont->saved_thread);
43
52
 
44
53
        if (cont->vm_stack) {
45
54
            rb_gc_mark_locations(cont->vm_stack,
46
55
                                 cont->vm_stack + cont->saved_thread.stack_size);
47
56
        }
48
 
        
 
57
 
49
58
        if (cont->machine_stack) {
50
59
            rb_gc_mark_locations(cont->machine_stack,
51
60
                                 cont->machine_stack + cont->machine_stack_size);
59
68
{
60
69
    FREE_REPORT_ENTER("cont");
61
70
    if (ptr) {
62
 
        rb_continuation_t *cont = ptr;
 
71
        rb_context_t *cont = ptr;
 
72
        FREE_UNLESS_NULL(cont->saved_thread.stack);
63
73
        FREE_UNLESS_NULL(cont->machine_stack);
64
74
        FREE_UNLESS_NULL(cont->vm_stack);
65
75
        ruby_xfree(ptr);
67
77
    FREE_REPORT_LEAVE("cont");
68
78
}
69
79
 
70
 
static VALUE
71
 
cont_capture(volatile int *stat)
 
80
static void
 
81
cont_save_machine_stack(rb_thread_t *th, rb_context_t *cont)
72
82
{
73
 
    rb_continuation_t *cont;
74
 
    VALUE contval;
75
 
    rb_thread_t *th = GET_THREAD(), *sth;
76
83
    int size;
77
84
 
78
 
    contval = Data_Make_Struct(rb_cCont, rb_continuation_t,
79
 
                               cont_mark, cont_free, cont);
80
 
 
81
 
    /* save context */
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);
87
 
 
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;
96
93
    }
97
94
 
98
 
    cont->machine_stack = ALLOC_N(VALUE, size);
 
95
    if (cont->machine_stack) {
 
96
        REALLOC_N(cont->machine_stack, VALUE, size);
 
97
    }
 
98
    else {
 
99
        cont->machine_stack = ALLOC_N(VALUE, size);
 
100
    }
 
101
 
99
102
    MEMCPY(cont->machine_stack, cont->machine_stack_src, VALUE, size);
 
103
}
 
104
 
 
105
static rb_context_t *
 
106
cont_new(VALUE klass)
 
107
{
 
108
    rb_context_t *cont;
 
109
    volatile VALUE contval;
 
110
    rb_thread_t *th = GET_THREAD(), *sth;
 
111
 
 
112
    contval = Data_Make_Struct(klass, rb_context_t,
 
113
                               cont_mark, cont_free, cont);
 
114
    cont->self = contval;
 
115
    cont->alive = Qtrue;
 
116
 
 
117
    /* save context */
 
118
    cont->saved_thread = *th;
 
119
    sth = &cont->saved_thread;
 
120
 
 
121
    return cont;
 
122
}
 
123
 
 
124
void th_stack_to_heap(rb_thread_t *th);
 
125
 
 
126
static VALUE
 
127
cont_capture(volatile int *stat)
 
128
{
 
129
    rb_context_t *cont;
 
130
    rb_thread_t *th;
 
131
 
 
132
    th_stack_to_heap(GET_THREAD());
 
133
    cont = cont_new(rb_cCont);
 
134
    th = &cont->saved_thread;
 
135
 
 
136
    cont->vm_stack = ALLOC_N(VALUE, th->stack_size);
 
137
    MEMCPY(cont->vm_stack, th->stack, VALUE, th->stack_size);
 
138
    th->stack = 0;
 
139
 
 
140
    cont_save_machine_stack(th, cont);
100
141
 
101
142
    if (ruby_setjmp(cont->jmpbuf)) {
102
 
        VALUE retval;
 
143
        VALUE value;
103
144
 
104
 
        retval = cont->retval;
105
 
        cont->retval = Qnil;
 
145
        value = cont->value;
 
146
        cont->value = Qnil;
106
147
        *stat = 1;
107
 
        return retval;
 
148
        return value;
108
149
    }
109
150
    else {
110
151
        *stat = 0;
111
 
        return contval;
 
152
        return cont->self;
112
153
    }
113
154
}
114
155
 
 
156
NORETURN(static void cont_restore_1(rb_context_t *));
 
157
 
115
158
static void
116
 
cont_restore_context_1(rb_continuation_t *cont)
 
159
cont_restore_1(rb_context_t *cont)
117
160
{
118
161
    rb_thread_t *th = GET_THREAD(), *sth = &cont->saved_thread;
119
162
 
120
163
    /* restore thread context */
121
 
    MEMCPY(th->stack, cont->vm_stack, VALUE, sth->stack_size);
 
164
    if (sth->stack) {
 
165
        /* fiber */
 
166
        th->stack = sth->stack;
 
167
        th->stack_size = sth->stack_size;
 
168
        th->fiber = cont->self;
 
169
    }
 
170
    else {
 
171
        /* continuation */
 
172
        MEMCPY(th->stack, cont->vm_stack, VALUE, sth->stack_size);
 
173
        th->fiber = sth->fiber;
 
174
    }
122
175
 
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;
129
185
 
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);
 
190
    }
133
191
 
134
192
    ruby_longjmp(cont->jmpbuf, 1);
135
193
}
136
194
 
137
 
NORETURN(NOINLINE(static void restore_context_0(rb_continuation_t *, VALUE *)));
 
195
NORETURN(NOINLINE(static void cont_restore_0(rb_context_t *, VALUE *)));
138
196
 
139
197
static void
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)
141
199
{
 
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];
144
203
 
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]);
148
 
    }
 
205
        if (addr_in_prev_frame > cont->machine_stack_src) {
 
206
            cont_restore_0(cont, &space[0]);
 
207
        }
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]);
152
 
    }
 
209
        if (addr_in_prev_frame < cont->machine_stack_src + cont->machine_stack_size) {
 
210
            cont_restore_0(cont, &space[STACK_PAD_SIZE-1]);
 
211
        }
153
212
#else
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]);
158
 
        }
159
 
    }
160
 
    else {
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]);
164
 
        }
165
 
    }
 
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]);
 
217
            }
 
218
        }
 
219
        else {
 
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]);
 
223
            }
 
224
        }
166
225
#endif
167
 
    cont_restore_context_1(cont);
 
226
    }
 
227
    cont_restore_1(cont);
168
228
}
169
229
 
170
230
/*
214
274
 *     3:  15 16
215
275
 */
216
276
 
217
 
VALUE rb_cCont;
218
 
 
219
277
/*
220
278
 *  call-seq:
221
279
 *     callcc {|cont| block }   =>  obj
244
302
    }
245
303
}
246
304
 
 
305
static VALUE
 
306
make_passing_arg(int argc, VALUE *argv)
 
307
{
 
308
    switch(argc) {
 
309
      case 0:
 
310
        return Qnil;
 
311
      case 1:
 
312
        return argv[0];
 
313
      default:
 
314
        return rb_ary_new4(argc, argv);
 
315
    }
 
316
}
 
317
 
247
318
/*
248
319
 *  call-seq:
249
320
 *     cont.call(args, ...)
263
334
static VALUE
264
335
rb_cont_call(int argc, VALUE *argv, VALUE contval)
265
336
{
266
 
    rb_continuation_t *cont;
 
337
    rb_context_t *cont;
267
338
    rb_thread_t *th = GET_THREAD();
268
339
    GetContPtr(contval, cont);
269
340
 
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");
272
343
    }
273
344
    if (cont->saved_thread.trap_tag != th->trap_tag) {
274
345
        rb_raise(rb_eRuntimeError, "continuation called across trap");
275
346
    }
 
347
    if (cont->saved_thread.fiber) {
 
348
        rb_context_t *fcont;
 
349
        GetContPtr(cont->saved_thread.fiber, fcont);
276
350
 
277
 
    switch(argc) {
278
 
      case 0:
279
 
        cont->retval = Qnil;
280
 
        break;
281
 
      case 1:
282
 
        cont->retval = argv[0];
283
 
        break;
284
 
      default:
285
 
        cont->retval = rb_ary_new4(argc, argv);
286
 
        break;
 
351
        if (!fcont->alive) {
 
352
            rb_raise(rb_eRuntimeError, "continuation called dead fiber");
 
353
        }
287
354
    }
288
355
 
289
 
    cont_restore_context_0(cont, (VALUE *)&cont);
 
356
    cont->value = make_passing_arg(argc, argv);
 
357
 
 
358
    cont_restore_0(cont, (VALUE *)&cont);
290
359
    return Qnil; /* unreachable */
291
360
}
292
361
 
 
362
/*********/
 
363
/* fiber */
 
364
/*********/
 
365
 
 
366
#define FIBER_STACK_SIZE (4 * 1024)
 
367
 
 
368
static VALUE
 
369
rb_fiber_s_new(VALUE self)
 
370
{
 
371
    rb_context_t *cont = cont_new(self);
 
372
    rb_thread_t *th = &cont->saved_thread;
 
373
 
 
374
    /* initialize */
 
375
    cont->prev = Qnil;
 
376
    cont->vm_stack = 0;
 
377
 
 
378
    th->stack = 0;
 
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);
 
382
    th->cfp--;
 
383
    th->cfp->pc = 0;
 
384
    th->cfp->sp = th->stack + 1;
 
385
    th->cfp->bp = 0;
 
386
    th->cfp->lfp = th->stack;
 
387
    *th->cfp->lfp = 0;
 
388
    th->cfp->dfp = th->stack;
 
389
    th->cfp->self = Qnil;
 
390
    th->cfp->magic = 0;
 
391
    th->cfp->iseq = 0;
 
392
    th->cfp->proc = 0;
 
393
    th->cfp->block_iseq = 0;
 
394
 
 
395
    th->first_proc = rb_block_proc();
 
396
 
 
397
    MEMCPY(&cont->jmpbuf, &th->root_jmpbuf, rb_jmpbuf_t, 1);
 
398
 
 
399
    return cont->self;
 
400
}
 
401
 
 
402
static VALUE rb_fiber_yield(int argc, VALUE *args, VALUE fval);
 
403
 
 
404
static void
 
405
rb_fiber_terminate(rb_context_t *cont)
 
406
{
 
407
    rb_context_t *prev_cont;
 
408
    VALUE value = cont->value;
 
409
 
 
410
    GetContPtr(cont->prev, prev_cont);
 
411
 
 
412
    cont->alive = Qfalse;
 
413
 
 
414
 
 
415
    if (prev_cont->alive == Qfalse) {
 
416
        rb_fiber_yield(1, &value, GET_THREAD()->root_fiber);
 
417
    }
 
418
    else {
 
419
        rb_fiber_yield(1, &value, cont->prev);
 
420
    }
 
421
}
 
422
 
 
423
void
 
424
rb_fiber_start(void)
 
425
{
 
426
    rb_thread_t *th = GET_THREAD();
 
427
    rb_context_t *cont;
 
428
    rb_proc_t *proc;
 
429
    VALUE args;
 
430
    int state;
 
431
 
 
432
    th->tag = 0;
 
433
 
 
434
    TH_PUSH_TAG(th);
 
435
    if ((state = EXEC_TAG()) == 0) {
 
436
        GetContPtr(th->fiber, cont);
 
437
        GetProcPtr(cont->saved_thread.first_proc, proc);
 
438
        args = cont->value;
 
439
        cont->value = Qnil;
 
440
        th->errinfo = Qnil;
 
441
        th->local_lfp = proc->block.lfp;
 
442
        th->local_svar = Qnil;
 
443
 
 
444
        cont->value = th_invoke_proc(th, proc, proc->block.self, 1, &args);
 
445
    }
 
446
    TH_POP_TAG();
 
447
 
 
448
    if (state) {
 
449
        th->thrown_errinfo = th_make_jump_tag_but_local_jump(state, th->errinfo);
 
450
        th->interrupt_flag = 1;
 
451
    }
 
452
 
 
453
    rb_fiber_terminate(cont);
 
454
    rb_bug("rb_fiber_start: unreachable");
 
455
}
 
456
 
 
457
static VALUE
 
458
rb_fiber_current(rb_thread_t *th)
 
459
{
 
460
    if (th->fiber == 0) {
 
461
        /* save root */
 
462
        th->root_fiber = th->fiber = cont_new(rb_cFiber)->self;
 
463
    }
 
464
    return th->fiber;
 
465
}
 
466
 
 
467
static VALUE
 
468
cont_store(rb_context_t *next_cont)
 
469
{
 
470
    rb_thread_t *th = GET_THREAD();
 
471
    rb_context_t *cont;
 
472
 
 
473
    if (th->fiber) {
 
474
        GetContPtr(th->fiber, cont);
 
475
        cont->saved_thread = *th;
 
476
    }
 
477
    else {
 
478
        /* create current fiber */
 
479
        cont = cont_new(rb_cFiber); /* no need to allocate vm stack */
 
480
        th->root_fiber = th->fiber = cont->self;
 
481
    }
 
482
 
 
483
    if (cont->alive) {
 
484
        next_cont->prev = cont->self;
 
485
    }
 
486
    cont_save_machine_stack(th, cont);
 
487
 
 
488
    if (ruby_setjmp(cont->jmpbuf)) {
 
489
        /* restored */
 
490
        GetContPtr(th->fiber, cont);
 
491
        return cont->value;
 
492
    }
 
493
    else {
 
494
        return Qundef;
 
495
    }
 
496
}
 
497
 
 
498
static VALUE
 
499
rb_fiber_yield(int argc, VALUE *argv, VALUE fval)
 
500
{
 
501
    VALUE value;
 
502
    rb_context_t *cont;
 
503
    rb_thread_t *th = GET_THREAD();
 
504
 
 
505
    GetContPtr(fval, cont);
 
506
 
 
507
    if (cont->saved_thread.self != th->self) {
 
508
        rb_raise(rb_eFiberError, "fiber called across threads");
 
509
    }
 
510
    if (cont->saved_thread.trap_tag != th->trap_tag) {
 
511
        rb_raise(rb_eFiberError, "fiber called across trap");
 
512
    }
 
513
    if (!cont->alive) {
 
514
        rb_raise(rb_eFiberError, "dead fiber called");
 
515
    }
 
516
 
 
517
    cont->value = make_passing_arg(argc, argv);
 
518
 
 
519
    if ((value = cont_store(cont)) == Qundef) {
 
520
        cont_restore_0(cont, (VALUE *)&cont);
 
521
        rb_bug("rb_fiber_yield: unreachable");
 
522
    }
 
523
 
 
524
    return value;
 
525
}
 
526
 
 
527
static VALUE
 
528
rb_fiber_prev(VALUE fval)
 
529
{
 
530
    rb_context_t *cont;
 
531
    GetContPtr(fval, cont);
 
532
    return cont->prev;
 
533
}
 
534
 
 
535
static VALUE
 
536
rb_fiber_alive_p(VALUE fval)
 
537
{
 
538
    rb_context_t *cont;
 
539
    GetContPtr(fval, cont);
 
540
    return cont->alive;
 
541
}
 
542
 
 
543
static VALUE
 
544
rb_fiber_s_current(VALUE klass)
 
545
{
 
546
    return rb_fiber_current(GET_THREAD());
 
547
}
 
548
 
 
549
static VALUE
 
550
rb_fiber_s_prev(VALUE klass)
 
551
{
 
552
    return rb_fiber_prev(rb_fiber_s_current(Qnil));
 
553
}
 
554
 
 
555
static VALUE
 
556
rb_fiber_s_yield(int argc, VALUE *argv, VALUE fval)
 
557
{
 
558
    return rb_fiber_yield(argc, argv, rb_fiber_s_prev(Qnil));
 
559
}
 
560
 
293
561
void
294
562
Init_Cont(void)
295
563
{
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);
 
570
 
 
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);
 
576
 
 
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);
 
581
 
 
582
    rb_eFiberError = rb_define_class("FiberError", rb_eStandardError);
302
583
}
303
584