Continuation objects are generated by
Kernel#callcc
. 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:
arr = [ "Freddie", "Herbie", "Ron", "Max", "Ringo" ] callcc{|$cc|} puts(message = arr.shift) $cc.call unless message =~ /Max/
produces:
Freddie Herbie Ron Max
This (somewhat contrived) example allows the inner loop to abandon processing early:
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 } print "\n"
produces:
0: 0 1 2 3 4 1: 5 6 7 8 9 2: 10 11 12 13 14 3: 15 16
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]
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.trap_tag != th->trap_tag) { rb_raise(rb_eRuntimeError, "continuation called across trap"); } if (cont->saved_thread.fiber) { rb_context_t *fcont; GetContPtr(cont->saved_thread.fiber, fcont); if (th->fiber != cont->saved_thread.fiber) { rb_raise(rb_eRuntimeError, "continuation called across fiber"); } } cont->argc = argc; cont->value = make_passing_arg(argc, argv); cont_restore_0(cont, &contval); return Qnil; /* unreachable */ }
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]
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.trap_tag != th->trap_tag) { rb_raise(rb_eRuntimeError, "continuation called across trap"); } if (cont->saved_thread.fiber) { rb_context_t *fcont; GetContPtr(cont->saved_thread.fiber, fcont); if (th->fiber != cont->saved_thread.fiber) { rb_raise(rb_eRuntimeError, "continuation called across fiber"); } } cont->argc = argc; cont->value = make_passing_arg(argc, argv); cont_restore_0(cont, &contval); return Qnil; /* unreachable */ }