guile-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: continuation efficiency


From: Marius Vollmer
Subject: Re: continuation efficiency
Date: 10 Jul 2001 01:24:00 +0200
User-agent: Gnus/5.09 (Gnus v5.9.0) Emacs/21.0.102

address@hidden (Thomas Bushnell, BSG) writes:

> More to the point, can someone provide an example that makes clear a
> situation where call/cc is *not* appropriate?

I can give an example where call/cc plus dynamic-wind is not
appropriate, in my opinion, and where I would rather use call/ec plus
escape-wind.  That is, it's the combination of the two procedures that
I'm arguing about, not so much call/cc in isolation.

Suppose you have two modules: one provides `with-remote-port', the
other uses it.  The procedure `with-remote-port' will open a new
network connection to some server and wrap it with a port.  This port
is then used by other code.  `with-remote-port' knows that the network
connection is expensive and ties up significant resources on the
server, and it would therefore like to close it reliably, and as early
as possible.

It does not want to rely on the garbage collector since it is
undefined when the GC will reclaim the port, if at all (or it doesn't
know about guardians or other means for finalization).

Also, `with-remote-port' doesn't want to deal with the error handling
system if it can avoid it, since it doesn't want to assume that all
non-local exits are associated with events that go thru the error
handling system, or interfere with the possibility of errors being
continued.

All it wants is a reliable notification that the dynamic extent of
`with-remote-port' has ended.

The other module has its own little implementation of its own version
of coroutines and uses `with-remote-port'.  `with-remote-port' wants
to cater to such modules, but it can't specifically coordinate with
each implementation of coroutines.  (Coroutines is not all what you
can do with continuations, but they are a very civilized use where you
can expect that the rest of the system can tolerate this use I think.
Other things are more nasty, like backtracking.  I don't think we can
expect that random code works well in the midst of backtracking, so
the backtracker needs to be careful.  It might be the same with
coroutines, but I like to think not.)

How should `with-remote-port' be written?  This version

    (define (with-remote-port proc)
      (let ((port (make-remote-port)))
        (proc port)
        (close-remote-port port)))

falls obviously short because when control leaves PROC non-locally,
PORT is not closed.  The improvement

    (define (with-remote-port proc)
      (let ((port (make-remote-port)))
        (dynamic-wind
          (lambda () #t)
          (lambda () (proc port))
          (lambda () (close-remote-port port)))))

has problems when control enters non-locally, like when a suspended
coroutine is resumed.  This

    (define (with-remote-port proc)
      (let ((port #f))
        (dynamic-wind
          (lambda () (lambda () (set! port (make-remote-port))))
          (lambda () (proc port))
          (lambda () (close-remote-port port)))))

is not really correct, since it opens a new remote port for each
resumption of the coroutine.  This will at best be wasteful, but will
more likely mess up the remote communication.  Disallowing re-entering
with

    (define (with-remote-port proc)
      (let ((port (make-remote-port))
            (exited? #f))
        (dynamic-wind
          (lambda () (if exited? (error "only one visit")))
          (lambda () (proc port))
          (lambda () (close-remote-port port) (set! exited? #t)))))

will ensure the proper operation of `with-remote-port', but will
simultaneously prohibit the use of call/cc in useful ways completely.


What I propose as the `right' solution is to use `escape-protect'
instead of `dynamic-wind' in the knowledge that `escape-protect' does
not interact with continuations created by `call/cc' but with ones
from `call/ec', and that everybody else uses continuations from
`call/ec' to exit non-locally.

    (define (with-remote-port proc)
      (let ((port (make-remote-port)))
        (escape-protect
          (lambda () (proc port))
          (lambda () (close-remote-port port)))))

With this code, coroutine suspension/resumption would be initiated by
continuations from call/cc, coroutine aborts or generally non-local
exits would be initiated by continuations from call/ec.


`call/ec' and `escape-protect' do only cover a small spectrum of the
many uses of continuations and might therefore not be worth to pursue.
However, whenever call/cc is used, care must be taken that all
participating code behaves correctly in the face of the `non-standard'
control flow forced upon it by call/cc.  Without special purpose
conventions like call/ec and escape protect, we can't even plan for
coroutines when writing library code.

Not very convincing, eh?



reply via email to

[Prev in Thread] Current Thread [Next in Thread]