Class: Continuation

Inherits:
Object show all
Defined in:
cont.c,
cont.c

Overview

Continuation objects are generated by Kernel#callcc, after having required continuation. They hold a return address and execution context, allowing a nonlocal return to the end of the callcc block from anywhere within a program. Continuations are somewhat analogous to a structured version of C’s setjmp/longjmp (although they contain more state, so you might consider them closer to threads).

For instance:

require "continuation"
arr = [ "Freddie", "Herbie", "Ron", "Max", "Ringo" ]
callcc{|cc| $cc = cc}
puts(message = arr.shift)
$cc.call unless message =~ /Max/

produces:

Freddie
Herbie
Ron
Max

Also you can call callcc in other methods:

require "continuation"

def g
  arr = [ "Freddie", "Herbie", "Ron", "Max", "Ringo" ]
  cc = callcc { |cc| cc }
  puts arr.shift
  return cc, arr.size
end

def f
  c, size = g
  c.call(c) if size > 1
end

f

This (somewhat contrived) example allows the inner loop to abandon processing early:

require "continuation"
callcc {|cont|
  for i in 0..4
    print "\n#{i}: "
    for j in i*5...(i+1)*5
      cont.call() if j == 17
      printf "%3d", j
    end
  end
}
puts

produces:

0:   0  1  2  3  4
1:   5  6  7  8  9
2:  10 11 12 13 14
3:  15 16

Instance Method Summary collapse

Instance Method Details

#call(args, ...) ⇒ Object #[](args, ...) ⇒ Object

Invokes the continuation. The program continues from the end of the callcc block. If no arguments are given, the original callcc returns nil. If one argument is given, callcc returns it. Otherwise, an array containing args is returned.

callcc {|cont|  cont.call }           #=> nil
callcc {|cont|  cont.call 1 }         #=> 1
callcc {|cont|  cont.call 1, 2, 3 }   #=> [1, 2, 3]


1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
# File 'cont.c', line 1068

static VALUE
rb_cont_call(int argc, VALUE *argv, VALUE contval)
{
    rb_context_t *cont;
    rb_thread_t *th = GET_THREAD();
    GetContPtr(contval, cont);

    if (cont->saved_thread.self != th->self) {
	rb_raise(rb_eRuntimeError, "continuation called across threads");
    }
    if (cont->saved_thread.protect_tag != th->protect_tag) {
	rb_raise(rb_eRuntimeError, "continuation called across stack rewinding barrier");
    }
    if (cont->saved_thread.fiber) {
	if (th->fiber != cont->saved_thread.fiber) {
	    rb_raise(rb_eRuntimeError, "continuation called across fiber");
	}
    }
    rollback_ensure_stack(contval, th->ensure_list, cont->ensure_array);

    cont->argc = argc;
    cont->value = make_passing_arg(argc, argv);

    /* restore `tracing' context. see [Feature #4347] */
    th->trace_arg = cont->saved_thread.trace_arg;

    cont_restore_0(cont, &contval);
    return Qnil; /* unreachable */
}

#call(args, ...) ⇒ Object #[](args, ...) ⇒ Object

Invokes the continuation. The program continues from the end of the callcc block. If no arguments are given, the original callcc returns nil. If one argument is given, callcc returns it. Otherwise, an array containing args is returned.

callcc {|cont|  cont.call }           #=> nil
callcc {|cont|  cont.call 1 }         #=> 1
callcc {|cont|  cont.call 1, 2, 3 }   #=> [1, 2, 3]


1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
# File 'cont.c', line 1068

static VALUE
rb_cont_call(int argc, VALUE *argv, VALUE contval)
{
    rb_context_t *cont;
    rb_thread_t *th = GET_THREAD();
    GetContPtr(contval, cont);

    if (cont->saved_thread.self != th->self) {
	rb_raise(rb_eRuntimeError, "continuation called across threads");
    }
    if (cont->saved_thread.protect_tag != th->protect_tag) {
	rb_raise(rb_eRuntimeError, "continuation called across stack rewinding barrier");
    }
    if (cont->saved_thread.fiber) {
	if (th->fiber != cont->saved_thread.fiber) {
	    rb_raise(rb_eRuntimeError, "continuation called across fiber");
	}
    }
    rollback_ensure_stack(contval, th->ensure_list, cont->ensure_array);

    cont->argc = argc;
    cont->value = make_passing_arg(argc, argv);

    /* restore `tracing' context. see [Feature #4347] */
    th->trace_arg = cont->saved_thread.trace_arg;

    cont_restore_0(cont, &contval);
    return Qnil; /* unreachable */
}