[plt-scheme] Re: Continuations memory accumulation problem

From: Ciprian Dorin, Craciun (ciprian.craciun at gmail.com)
Date: Mon Mar 15 08:08:45 EDT 2010

    It seems that I've fixed the problem with the memory leak. See
below for the changed functions.

    What I've changed:
    * so my `call-with-current-continuation` was not in a tail
position (it was inside a `set`, and followed by a `(void)` at the end
of the function));
    * I've made the `call/cc` in tail position and moved the `set`
inside the new lambda;
   * the code got uglier, and less clear; I liked the old code better,
and the fact that it was clear that the `call/cc` exits with a new
continuation.

    But I still don't get it where the memory leak is from:
    * all my functions are not recursive (except the `do-robot!`), and
so continuations couldn't just pile up in the context of recursive
calls;
    * also there are only two continuations: the current one running,
and the one to return to when interrupting... (except maybe only the
one in `start-robot!`;)

    Could someone explain this to me?

    Thanks,
    Ciprian.


~~~~ from this
(define (enter-robot! robot)

       (define continuation-tag (robot-continuation-tag robot))
       (define old-behaviour-continuation (robot-continuation robot))

       (set-robot-continuation! robot #f)
       (set-robot-continuation! robot
               (call-with-continuation-prompt
                       (lambda ()
                               (call-with-current-continuation
                                       old-behaviour-continuation
                                       continuation-tag))
                       continuation-tag))

       (void))
~~~~ to this
(define (enter-robot! robot)
	
	(call-with-continuation-prompt
		(lambda ()
			(call-with-current-continuation
				(lambda (new-framework-continuation)
					(define old-behaviour-continuation (robot-continuation robot))
					(set-robot-continuation! robot new-framework-continuation)
					(old-behaviour-continuation (void)))
				(robot-continuation-tag robot)))
		(robot-continuation-tag robot)))
~~~~



~~~~ and everything put together
~~~~
(define (start-robot! robot)
	
	(call-with-continuation-prompt
		(lambda ()
			(call-with-current-continuation
				(lambda (framework-continuation)
					(set-robot-continuation! robot framework-continuation)
					(set! framework-continuation (void))
					(exit-robot! robot)
					(do-robot! robot)
					(set! framework-continuation (robot-continuation robot))
					(set-robot-continuation! robot #f)
					(framework-continuation (void)))
				(robot-continuation-tag robot)))
		(robot-continuation-tag robot)))

(define (enter-robot! robot)
	
	(call-with-continuation-prompt
		(lambda ()
			(call-with-current-continuation
				(lambda (new-framework-continuation)
					(define old-behaviour-continuation (robot-continuation robot))
					(set-robot-continuation! robot new-framework-continuation)
					(old-behaviour-continuation (void)))
				(robot-continuation-tag robot)))
		(robot-continuation-tag robot)))

(define (exit-robot! robot)
	
	(call-with-current-continuation
		(lambda (new-behaviour-continuation)
			(define old-framework-continuation (robot-continuation robot))
			(set-robot-continuation! robot new-behaviour-continuation)
			(old-framework-continuation (void)))
		(robot-continuation-tag robot)))
~~~~


On Sun, Mar 14, 2010 at 10:47 PM, Ciprian Dorin, Craciun
<ciprian.craciun at gmail.com> wrote:
>    Hello all!
>
>    I have a small problem with continuations, more exactly memory
> builds up and I don't seem to find the problem... (This is the first
> time I seriously play with continuations so if I'm doing something
> really stupid please bare with me...) :)
>
>    So maybe someone has 5 minutes to look at my code... :)
>
>    What I'm trying to do: re-implement GnuRobots for PLT Scheme.
> (Actually it's done but I have this issue with the continuations.)
>
>    So how I've implemented it (the code presented is extracted from
> my real code, but this snippet actually works on itself if one wants
> to try it):
>    * there is a timer that calls the `step-robots!` function; (here
> the `step-robots!` just recurses with no timeout;)
>    * each robot has a behaviour (in this case `do-robot!`) function
> which should after each robot-operation yield control back to the
> framework; (this way the robot behaviour can be very easily
> implemented as a tail recursive function);
>    * in order to obtain this, I first capture a continuation just
> before calling the behaviour function (in this case `do-robot!`);
>    * then at each step I capture the current continuation, dive in in
> the last saved continuation from `do-robot!`;
>    * when the `do-robot!` function calls `exit-robot!` (this is the
> yield function) I just save the current continuation and fall-back to
> the old one (which is just inside the stepping loop);
>
>    But I'm certainly doing something wrong as memory adds up and is
> freed only after a robot dies (meaning it exits the `do-robot!`) (and
> I manually call `(collect-garbage)` inside the `step-robots!`).
> Unfortunately I don't seem to find the bug...
>
>    Thanks for your time!
>    Ciprian.
>
>    P.S.: I intend to share the source code under GPL... :)
>
>    P.P.S.: I've also attached the file because Gmail tends to wrap
> lines badly...
>
>
> ~~~~
> #lang scheme
>
>
> (define-struct robot
>        (continuation continuation-tag) #:mutable)
>
> (define (create-robot)
>        (make-robot (void) (make-continuation-prompt-tag)))
>
>
> (define (step-robots! robots)
>
>        (for ((robot (in-list robots)))
>                (cond
>                        ((void? (robot-continuation robot))
>                                (start-robot! robot))
>                        ((continuation? (robot-continuation robot))
>                                (enter-robot! robot))
>                        (else
>                                (display 'died)
>                                (newline))))
>
>        (step-robots! robots))
>
> (define (start-robot! robot)
>
>        (define continuation-tag (robot-continuation-tag robot))
>
>        (set-robot-continuation! robot #f)
>        (set-robot-continuation! robot
>                (call-with-continuation-prompt
>                        (lambda ()
>                                (call-with-current-continuation
>                                        (lambda (new-framework-continuation)
>                                                (set-robot-continuation! robot new-framework-continuation)
>                                                (exit-robot! robot)
>                                                (do-robot! robot)
>                                                (done-robot! robot))
>                                        continuation-tag))
>                        continuation-tag))
>
>        (void))
>
> (define (enter-robot! robot)
>
>        (define continuation-tag (robot-continuation-tag robot))
>        (define old-behaviour-continuation (robot-continuation robot))
>
>        (set-robot-continuation! robot #f)
>        (set-robot-continuation! robot
>                (call-with-continuation-prompt
>                        (lambda ()
>                                (call-with-current-continuation
>                                        old-behaviour-continuation
>                                        continuation-tag))
>                        continuation-tag))
>
>        (void))
>
> (define (exit-robot! robot)
>
>        (define continuation-tag (robot-continuation-tag robot))
>        (define old-framework-continuation (robot-continuation robot))
>
>        (set-robot-continuation! robot #f)
>        (set-robot-continuation! robot
>                (call-with-current-continuation
>                        old-framework-continuation
>                        continuation-tag))
>
>        (void))
>
> (define (done-robot! robot)
>
>        (define old-framework-continuation (robot-continuation robot))
>
>        (set-robot-continuation! robot #f)
>
>        (old-framework-continuation #f))
>
> (define (do-robot! robot (steps 100000))
>        (unless (zero? steps)
>                (exit-robot! robot)
>                (do-robot! robot (- steps 1))))
>
>
> (define robots (list (create-robot) (create-robot) (create-robot)))
>
> (step-robots! robots)
>


Posted on the users mailing list.