[gambit-list] safe dynamic-wind

Christian christian at pflanze.mine.nu
Sat Jan 7 10:24:06 EST 2006


At 21:30 Uhr +0100 04.01.2006, Christian wrote:
>I know this implementation of parameterize has a problem though:
>dynamic-wind is not safe when using exceptions. Why? I find this
>especially strange considering that I thought with-exception-catcher
>was implemented using continuations.

Here's an example, taken and adapted from
http://sisc.sourceforge.net/manual-beta/ch03.html#ErrorHandling

but using the more lowlevel with-exception-handler instead of 
with-exception-catcher:

(define (test capture call)
   (let* ((x 0)
	(result
	  (capture
	   (lambda(cont)
	     (with-exception-handler
	      (lambda(e)
		(call cont e))
	      (lambda()
		(dynamic-wind
		    (lambda () (set! x (+ x 1)))
		    (lambda () (/ 1 0))
		    (lambda () (set! x (+ x 1))))))))))
     (values result x)))

; > (load "bug-dynamic-wind")
; "/home/chris/schemedevelopment/gambit/mod/maybe-bug-dynamic-wind.scm"
; > (test call/cc (lambda (cont e) (cont e)))
; #<divide-by-zero-exception #2>
; 2
; > (test ##continuation-capture (lambda (cont e) 
(##continuation-return cont e)))
; #<divide-by-zero-exception #3>
; 1
; > (test ##continuation-capture (lambda (cont e) 
(##continuation-graft cont (lambda() e))))
; #<divide-by-zero-exception #4>
; 1

So:
- if I implement my own catcher using R5RS call/cc, it works as I 
expected and as SISC works.
- if I implement my own catcher using raw lowlevel continuations, it 
does work (or "not work") like with-exception-catcher.

Do I understand correctly that the raw lowlevel continuation 
mechanism is dynamic-scope unaware, and the implementation of 
call-with-current-continuation (in _thread.scm, I don't currently 
understand enough of it) has to take special steps for making 
dynamic-wind work?

If so: why are those not used in the exception catcher 
implementation, because of efficiency? BTW entering ,d into a nested 
repl jumps to the enclosing repl with an unsafe jump as well.

I've tried to come up with a safe variant of dynamic-wind:

(define (safer-dynamic-wind before main after)
   (let ((old-handler (current-exception-handler)))
     (with-exception-handler
      (lambda (e)
        ;; install old-handler to prevent infinite loops
        ;; from exceptions in after thunk
        (current-exception-handler old-handler)
        ;; (this shadows the original error though, which is bad too;
        ;; todo: combine errors into multi-exception objects, or output
        ;; a warning or something similar.)
        (after)
        (let ((result (old-handler e)))
	(before)
	result))
      (lambda ()
        (dynamic-wind
	   before
	   main
	   after)))))

; > (safer-dynamic-wind (lambda () (println "before")) (lambda () (+ 
(error "hello") 1)) (lambda () (println "after")))
; before
; after
; *** ERROR IN #<procedure #12>, "maybebug-dynamic-wind.scm"@134.22 -- hello
; 1> ,(c 100)
; before
; after
; 101
; > (safer-dynamic-wind (lambda () (println "before")) (lambda () (+ 
(error "hello") 1)) (lambda () (println "after")))
; before
; after
; *** ERROR IN #<procedure #13>, "maybebug-dynamic-wind.scm"@134.22 -- hello
; 1> #||#,+;
; 1  #<procedure #14>          (stdin)@35.66           (error "hello")
; 1\1> ,(c 101)
; after
; 102
; >

So it helps as long as I don't short-cut away my continuation in the 
repl. It's also rather ugly that I don't immediately see the position 
of the code where the error happened when being thrown into the 
debugger. (For inspecting the lexical environment of the place where 
the debugger happens I'll also step one up, and then it's too easy to 
forget to step back before continuing.)

At least for the repl, efficiency is no concern - could it be changed 
to use safe continuations?

Christian.



More information about the Gambit-list mailing list