Learn how easy it is to sync an existing GitHub or Google Code repo to a SourceForge project! See Demo


#1 Continuations: segmentation fault and wrong counting



The attached program show two bugs of TinyScheme.

Executing the infinite loop that calls (count-test),
the output should be:


instead with TinyScheme the program print 0, then
continuosly the number 1.

Executing the infinite loop that calls (segfault-test),
a segmentation fault happens (usually after few
seconds, sometimes it don't crash so try different
runs). Using (gc-verbose #t) is possible to see a
strange behaviour of GC: while in the first (do) (the
one that print "a") the GC is stable (recovering the
same amount of cells at each invokation), but when the
program enter in the second (do) (the one that print
"b") the GC become instable (the number of recovered
cells decrease continuosly) and after few seconds it crash.

The same program works correctly using both Guile and



  • Sample test to see both the segmentation-fault than the failed counting.

  • Logged In: NO

    I tried count-test with mini-scheme0.85.
    Because "do" syntax was not able to be used, it converts it
    into the following codes.

    (define count-test
    (lambda (yield)
    (let loop ((i 0))
    (display i)(newline)
    (yield #t)
    (loop (+ 1 i))))))

    In mini-scheme, the result of "0 1 2 3 4 ..." was obtained.
    In TinyScheme, it was "0 1 1 1 1 ..."

  • Kevin Cozens
    Kevin Cozens

    Logged In: YES
    Originator: NO

    The output from (count-test) using guile-1.8.0-8.20060831cvs in Fedora Core 6 only gave the following output:

  • Logged In: NO

    There seems to be a problem in the restart of "continuation".
    TinyScheme 1.37
    > (define r #f)
    (+ 1 (call/cc
    (lambda (k)
    (set! r k)
    (k 3))))
    > (r 5)
    Error: illegal function

    > r

  • Logged In: NO

    The problem is still present with version 1.39.

  • Tracing the continuation problem yields the following:

    > (define r #f)

    Eval: (define r #f)
    Eval: #f
    Gives: r
    > (+ 1 (call/cc (lambda (k) (set! r k) (k 3))))

    Eval: (+ 1 (call/cc (lambda (k) (set! r k) (k 3))))
    Eval: +
    Eval: 1
    Eval: (call/cc (lambda (k) (set! r k) (k 3)))
    Eval: call/cc
    Eval: (lambda (k) (set! r k) (k 3))
    Apply to: (#<call-with-current-continuation PROCEDURE 50> #<CLOSURE>)
    Apply to: (#<CLOSURE> #<CONTINUATION>)
    Eval: (set! r k)
    Eval: k
    Eval: (k 3)
    Eval: k
    Eval: 3
    Apply to: (#<CONTINUATION> 3)
    Apply to: (#<+ PROCEDURE 66> 1 3)
    Gives: 4
    > (r 5)

    Eval: (r 5)
    Eval: r
    Eval: 5
    Apply to: (#<CONTINUATION> 5)
    Apply to: (3 1 5)
    Eval: (#<CLOSURE> "illegal function")
    Eval: #<CLOSURE>
    Eval: "illegal function"
    Apply to: (#<CLOSURE> "illegal function")
    Eval: (if (more-handlers?) (apply (pop-handler)) (apply error x))

    Notice the line where it says
    Apply to: (3 1 5)

    This should be
    Apply to: (#<+ PROCEDURE 66> 1 5)

    because that's the context of the continuation. Somehow TinyScheme "forgets" that it should apply the result to a continuation and instead tries to apply it to a number, which (of course) fails.

    Typing (r 23) afterwards yields this:
    > (r 23)

    Eval: (r 23)
    Eval: r
    Eval: 23
    Apply to: (#<CONTINUATION> 23)
    Apply to: (5 1 23)
    Eval: (#<CLOSURE> "illegal function")
    Eval: #<CLOSURE>
    Eval: "illegal function"
    Apply to: (#<CLOSURE> "illegal function")
    Eval: (if (more-handlers?) (apply (pop-handler)) (apply error x))
    Eval: (more-handlers?)
    Eval: more-handlers?

    Apply to: (5 1 23)

    So instead of taking the "correct" context from the continuation, TinyScheme always takes the "last" context and tries to apply it's values there. I.e. try this:

    (define r #f)
    (+ 1 (call/cc (lambda (k) (set! r k) (k 3))))
    (r 23)
    (r 42)

    this tries to apply the following:
    (r 23)
    Apply to: (3 1 23)
    (r 42)
    Apply to: (23 1 23)

    However, I think I'm (currently) unable to provide a patch to fix this. Maybe someone else can take a look?

  • This error is due to the use of a destructive update of sc->args in the state OP_E1ARGS (scheme.c:2586) which the continuations 'return' to, effectively clobbering the state of the continuation by doing so. I'm not sure what the best thing to do to prevent this is - destructive reversing is spread through several other types of syntax (let, etc.), and thus far I've been unable to get tinyscheme to properly duplicate arguments when resuming (even inserting my own s_resume which flags s_return to duplicate sc->args, AND duplicating these in mk_continuation doesn't seem to fix the problem, so apparently there is some other reference/process which continues clobbering these arguments even then.) Hopefully someone more knowledgeable about tinyscheme than I am can put this knowledge to good use.

  • Sorry, it was the last test case posted in the comments which only involves the one mutating reverse operation - the file test.scm which is attached to the report itself requires the let binding states to use reverse instead of reverse_in_place as well. My problem still stands with properly duplicating things on the dump stack though - there still remains some access which I can't account for which foils my attempts to cut down on the memory bandwidth hit caused by the fix.

  • Ah, found it. Of course, not only the single set of args is being modified, but every set up the stack until the top-level is reached - thus all continuations must be duplicated (and have their args duplicated recursively) before being spliced back onto the stack. The attached patch provides a fix for all of the test cases presented here to my knowledge, though I only added a no-op to the non-scheme stack based implementation for cloning stacks.

  • Kevin Cozens
    Kevin Cozens

    The patch file referenced in the previous comment has been attached to patch report #5 and was updated for use with the 1.40 version of TinyScheme.