# class Continuation

`Continuation` objects are generated by `Kernel#callcc`, after having +require+d 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 "#{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

### Public Instance Methods

cont[args, ...] click to toggle source

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 = cont_ptr(contval);
        rb_thread_t *th = GET_THREAD();

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

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

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

call(args, ...) click to toggle source

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 = cont_ptr(contval);
        rb_thread_t *th = GET_THREAD();

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

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

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