Re: [Chicken-users] Threads and dynamic-wind still problematic
John Cowan wrote: Tobia Conforto scripsit: I wonder how other compilers do it. For example, I find Java's (and Python's) try/finally syntax quite useful. I've always thought dynamic-wind was Scheme's equivalent construct, but it would appear I was mistaken. Here's the relevant writeup from Taylor Campbell's (Riastradh's) blag, slightly edited. Thank you, very instructive. (UNWIND-PROTECT form protection-form ...), a special form, evaluates the first form and returns its value, but before returning the value also executes the protection forms. Furthermore, if a throw in the primary form transfers control to outside the whole UNWIND-PROTECT form, this, too, will execute the protection forms. This is a restricted definition of what unwind-protect does in CL. It doesn't just execute the protection forms before a normal return and an exception, but also before return-from, go, and other kinds of non- local exits that in Scheme would be coded with call/cc. But if the definition above (executing the finalizers before normal returns and exceptions only) is all that matters, it's not hard to write: (define (unwind-protect thunk finalizer) (let* ((original-e-h (current-exception-handler)) (result (with-exception-handler (lambda (exn) (with-exception-handler original-e-h finalizer) (original-e-h exn)) thunk))) (finalizer) result)) Or if you care for multiple values: (define (unwind-protect thunk finalizer) (let ((original-e-h (current-exception-handler))) (call-with-values (lambda () (with-exception-handler (lambda (exn) (with-exception-handler original-e-h finalizer) (original-e-h exn)) thunk)) (lambda vals (finalizer) (apply values vals) I believe this is much more of a Chicken version of try/finally than dynamic-wind is. Tobia ___ Chicken-users mailing list Chicken-users@nongnu.org http://lists.nongnu.org/mailman/listinfo/chicken-users
Re: [Chicken-users] Threads and dynamic-wind still problematic
On Tue, Mar 10, 2009 at 04:43:00PM -0500, Jim Ursetto wrote: > Hi Peter. You might disagree with the rationale, but the behavior is > in fact consistent--your example only happens to work at the REPL. Jim, thanks for that explanation. It cleared up a lot! Cheers, Peter -- http://sjamaan.ath.cx -- "The process of preparing programs for a digital computer is especially attractive, not only because it can be economically and scientifically rewarding, but also because it can be an aesthetic experience much like composing poetry or music." -- Donald Knuth pgpplx45W9I3f.pgp Description: PGP signature ___ Chicken-users mailing list Chicken-users@nongnu.org http://lists.nongnu.org/mailman/listinfo/chicken-users
Re: [Chicken-users] Threads and dynamic-wind still problematic
On Mon, Mar 2, 2009 at 3:48 PM, Peter Bex wrote: > However, it turns out that an exception thrown in a thread simply > terminates the thread instead of unwinding the stack. The same does not > happen in the primordial thread: > $ csi > #;2> (dynamic-wind (lambda () (print "BEFORE")) (lambda () (error "die!")) > (lambda () (print "AFTER"))) > BEFORE > Error: die! > AFTER Hi Peter. You might disagree with the rationale, but the behavior is in fact consistent--your example only happens to work at the REPL. Try compiling that or calling it with `csi -eval`, and you will see the after thunk is not invoked. When you are not at a REPL, the default error handler will immediately terminate the program upon error, or break into a repl if you've enabled that option; the after thunks are not invoked. When you *are* at a REPL, the default error handler is overridden to print an error message without terminating, and then the reset handler is called. The magic comes in when, before evaluating your expression, the REPL captures the current continuation and overrides the reset handler to invoke it. When an error occurs, that continuation is invoked to return to the REPL, *and* by doing so, your dynamic-wind after thunks are invoked. Note that this is exactly what Marc Feeley prescribed: "If the programmer knows how to recover from an exception, then he can capture the continuation early on, and install an exception handler which invokes the continuation. When the continuation is invoked the after thunks will execute." It just happens that at a REPL, exception recovery corresponds to "continue with the REPL". Here is a little program which demonstrates in essence what the repl is doing. Compile with `csc x.scm`. (define (simulate-repl thunk) (let ((eh (##sys#error-handler)) (rh (##sys#reset-handler))) (dynamic-wind (lambda () (##sys#error-handler (lambda (msg . args) (apply print "Error: " msg args (lambda () (let ((k (call/cc (lambda (c) (##sys#reset-handler (lambda () (c #f))) (if k (thunk (lambda () (##sys#error-handler eh) (##sys#reset-handler rh) (define (test) (dynamic-wind (lambda () (print "BEFORE")) (lambda () (error "die!")) (lambda () (print "AFTER" (simulate-repl test) (test) (test) ; not reached #| Output BEFORE Error: die! AFTER BEFORE Error: die! |# ___ Chicken-users mailing list Chicken-users@nongnu.org http://lists.nongnu.org/mailman/listinfo/chicken-users
Re: [Chicken-users] Threads and dynamic-wind still problematic
On Sun, Mar 8, 2009 at 12:34 PM, Peter Bex wrote: > On Sun, Mar 08, 2009 at 12:10:39AM +0100, felix winkelmann wrote: >> >> Ok, here is what Marc Feeley, the author of SRFI-18 has to say >> about this: > > >> >> >Hope that clarifies things. >> >> Hope that clarifies things. > > Yup, thanks for asking him. It still sucks, though ;) > Somewhat understandable. I will add the explanation of Marc to the manual/FAQ. cheers, felix ___ Chicken-users mailing list Chicken-users@nongnu.org http://lists.nongnu.org/mailman/listinfo/chicken-users
Re: [Chicken-users] Threads and dynamic-wind still problematic
Tobia Conforto scripsit: > I wonder how other compilers do it. For example, I find Java's (and > Python's) try/finally syntax quite useful. I've always thought > dynamic-wind was Scheme's equivalent construct, but it would appear I > was mistaken. Here's the relevant writeup from Taylor Campbell's (Riastradh's) blag, slightly edited. DYNAMIC-WIND has long been a source of contention in the Lisp world. It serves a very particular purpose, but this purpose is not very well understood, and many think that this purpose is the same as the purpose of other Lisps' UNWIND-PROTECT. While the general idea of UNWIND-PROTECT is useful, it is not directly applicable in Scheme, because Scheme has more powerful control abstractions than other Lisps', and it therefore requires more powerful means of controlling them. (UNWIND-PROTECT form protection-form ...), a special form, evaluates the first form and returns its value, but before returning the value also executes the protection forms. Furthermore, if a throw in the primary form transfers control to outside the whole UNWIND-PROTECT form, this, too, will execute the protection forms. This allows programmers to *reliably* clean things up at certain points in the program -- for instance, to close open files, to shut down sockets, to release database handles, &c.; here is hypothetical example of its use: (define (call-with-input-file pathname procedure) (let ((input-port (open-input-file pathname))) (unwind-protect (procedure input-port) (close-input-port input-port (DYNAMIC-WIND before during after), a procedure of three procedural parameters, calls the during thunk, ensuring that any transfer of control into it calls the before thunk, and that any transfer of control out of it calls the after thunk. Normal control transfers, i.e. the initial call to the during thunk and its final return, qualify as control transfers too. This means that, any time control is inside the dynamic extent of the during thunk, any state established by the before thunk will be in effect, but any time control is outside its dynamic extent, that state will be torn down by the after thunk. Here is an actual example of DYNAMIC-WIND's use, from the Edwin text editor: (define (with-current-local-bindings! thunk) (dynamic-wind (lambda () (install-buffer-local-bindings! (current-buffer))) thunk (lambda () (uninstall-buffer-local-bindings! (current-buffer) This ensures that, within THUNK, all Edwin variables have any values specified by the buffer's local bindings. This is the kind of use that DYNAMIC-WIND is appropriate for: a sort of control bracket that ensures the presence of certain dynamic state or context within a certain dynamic extent. Now, unfortunately, DYNAMIC-WIND can be seen as somewhat similar to UNWIND-PROTECT, because for basic cases (involving no sophisticated exploitation of Scheme's continuation reification) DYNAMIC-WIND with a null entrance thunk seems to be equivalent to UNWIND-PROTECT. This is misleading, however. Either control may return to the dynamic extent of the during thunk in the DYNAMIC-WIND, in which case the after thunk will be called twice -- which is unacceptably wrong --, or the before thunk really signals an error if it is called twice. This, though, violates the philosophy described in the first sentence of the R5RS: Programming languages should be designed not by piling feature on top of feature, but by removing the weaknesses and restrictions that make additional features appear necessary. Signalling an error in a DYNAMIC-WIND before thunk is usually a limitation and a weakness in a program. We have an *extremely* powerful means of abstraction of control -- CALL/CC[1] -- and we ought not to prevent it from working merely because we want to prevent a certain fragment of code from running more than once. This should, however, spur a deeper look at why the code is being run more than once in the first place. DYNAMIC-WIND is meant to establish local dynamic state for specific dynamic extents within programs, by setting it up in before thunks and tearing it down in after thunks. These operations, to set up and to tear down dynamic state, are meant to be reversible, local changes.[2] That is, permanent actions, such as closing a port, that one would put in a protection form of an UNWIND-PROTECT, are entirely inappropriate in a DYNAMIC-WIND after thunk. Yet the basic idea of UNWIND-PROTECT is still useful. What, exactly, though, is useful from UNWIND-PROTECT, and what is incompatible with Scheme? The concept of permanent finalization is useful, and the identification of a good point to perform it is useful. *Requiring* that it be performed at this point, though, is incompatible with Scheme, because this point m
Re: [Chicken-users] Threads and dynamic-wind still problematic
Peter Bex wrote: Yup, thanks for asking him. It still sucks, though ;) It does. This is one way to solve it: #;1> (use srfi-18) ; loading library srfi-18 ... #;2> (define exception-protect ---> (let ((original-exception-handler (current-exception-handler))) ---> (lambda (thunk) ---> (lambda () ---> (with-exception-handler original-exception-handler thunk) #;3> (define (test) ---> (dynamic-wind ---> (lambda () ---> (print "BEFORE")) ---> (lambda () ---> (error "die!") ---> (print "NOT PRINTING")) ---> (lambda () ---> (print "AFTER" #;4> (thread-join! (thread-start! (exception-protect test))) BEFORE Error: die! Call history: . . . AFTER #;4> The key is defining exception-protect in the primordial thread. Then you can use it wherever you need it. I'm not knowledgeable enough to wager what kinds of problems it may cause... and I have a feeling it may cause lots! Or maybe not. So use it at your own risk. -Tobia ___ Chicken-users mailing list Chicken-users@nongnu.org http://lists.nongnu.org/mailman/listinfo/chicken-users
Re: [Chicken-users] Threads and dynamic-wind still problematic
felix winkelmann wrote: Ok, here is what Marc Feeley, the author of SRFI-18 has to say about this: when an uncaught exception occurs in a thread the thread is in bad shape and things have gone sufficiently wrong that there is no universally acceptable way to continue execution. Executing after thunks could require a whole lot of processing that the thread is not in a shape to do. So the safe thing is to terminate the thread. Is this argument specific to Chicken threads? I wonder how other compilers do it. For example, I find Java's (and Python's) try/finally syntax quite useful. I've always thought dynamic-wind was Scheme's equivalent construct, but it would appear I was mistaken. -Tobia ___ Chicken-users mailing list Chicken-users@nongnu.org http://lists.nongnu.org/mailman/listinfo/chicken-users
Re: [Chicken-users] Threads and dynamic-wind still problematic
Peter Bex wrote: Yup, thanks for asking him. It still sucks, though ;) This is yet another solution, in case you can't (or don't want to) steal the exception handler from the primordial thread. This one may be safer. But then again, I'm probably missing Marc Feeley's point completely, so it might not be safe to use at all :-) #;1> (use srfi-18) ; loading library srfi-18 ... #;2> (define (exception-protect thunk) ---> (define exn-message (condition-property-accessor 'exn 'message)) ---> (lambda () ---> (call-with-current-continuation ---> (lambda (quit) ---> (with-exception-handler ---> (lambda (exn) ---> (print "Error: " (exn-message exn)) ---> (quit)) ---> thunk) #;3> (define (test) ---> (dynamic-wind ---> (lambda () ---> (print "BEFORE")) ---> (lambda () ---> (error "die!") ---> (print "OH NOES")) ---> (lambda () ---> (print "AFTER" #;4> (thread-join! (thread-start! (exception-protect test))) BEFORE Error: die! AFTER #;5> Tobia ___ Chicken-users mailing list Chicken-users@nongnu.org http://lists.nongnu.org/mailman/listinfo/chicken-users
Re: [Chicken-users] Threads and dynamic-wind still problematic
On Sun, Mar 8, 2009 at 12:34 PM, Peter Bex wrote: > > Yup, thanks for asking him. It still sucks, though ;) > We are bound to the SRFI-18 spec, but you are free to invent your own API. cheers, felix ___ Chicken-users mailing list Chicken-users@nongnu.org http://lists.nongnu.org/mailman/listinfo/chicken-users
Re: [Chicken-users] Threads and dynamic-wind still problematic
On Sun, Mar 08, 2009 at 12:10:39AM +0100, felix winkelmann wrote: > > Ok, here is what Marc Feeley, the author of SRFI-18 has to say > about this: > > >Hope that clarifies things. > > Hope that clarifies things. Yup, thanks for asking him. It still sucks, though ;) Cheers, Peter -- http://sjamaan.ath.cx -- "The process of preparing programs for a digital computer is especially attractive, not only because it can be economically and scientifically rewarding, but also because it can be an aesthetic experience much like composing poetry or music." -- Donald Knuth pgpMl8X76VbKO.pgp Description: PGP signature ___ Chicken-users mailing list Chicken-users@nongnu.org http://lists.nongnu.org/mailman/listinfo/chicken-users
Re: [Chicken-users] Threads and dynamic-wind still problematic
On Mon, Mar 2, 2009 at 9:48 PM, Peter Bex wrote: > > However, it turns out that an exception thrown in a thread simply > terminates the thread instead of unwinding the stack. The same does not > happen in the primordial thread: > Ok, here is what Marc Feeley, the author of SRFI-18 has to say about this: >>I have a question about SRFI-18: the default exception handler >> for a thread (the one that stores the thrown object in the >> thread to be later accessed via thread-join!), should it invoke >> dynamic-wind after thunks? > >No the default exception handler shouldn't invoke the after thunks of the >current continuation. That's because the >exception handler doesn't >"continue" at the initial continuation of that thread. Here are the relevant >words of SRFI >18: > > Moreover, in this dynamic environment the exception handler > is bound to the "initial exception handler" which is a unary > procedure which causes the (then) current thread to store in > its end-exception field an "uncaught exception" object whose > "reason" is the argument of the handler, abandon all mutexes > it owns, and finally terminate. > >The rationale is that, when an uncaught exception occurs in a thread the >thread is in bad shape and things have >gone sufficiently wrong that there is >no universally acceptable way to continue execution. Executing after thunks >>could require a whole lot of processing that the thread is not in a shape to >do. So the safe thing is to terminate >the thread. If the programmer knows >how to recover from an exception, then he can capture the continuation >early >on, and install an exception handler which invokes the continuation. When the >continuation is invoked the >after thunks will execute. > >Hope that clarifies things. Hope that clarifies things. cheers, felix ___ Chicken-users mailing list Chicken-users@nongnu.org http://lists.nongnu.org/mailman/listinfo/chicken-users
Re: [Chicken-users] Threads and dynamic-wind still problematic
On Mon, Mar 2, 2009 at 9:48 PM, Peter Bex wrote: > Hi, > > I just got bitten by this problem: > http://lists.gnu.org/archive/html/chicken-users/2008-04/msg00023.html > > I'm increasing and decreasing a mutex value based on how many threads > are doing some work. When they stop working, the mutex is decreased > and when they start, the mutex is increased. I decided to wrap the > thunk that does the work in a dynamic-wind to protect it from escape > continuations being called, and from exceptions. > > However, it turns out that an exception thrown in a thread simply > terminates the thread instead of unwinding the stack. The same does not > happen in the primordial thread: > I think the SRFI-18 specification isn't quite clear about this. It only specifies that the default exception handler stores the exception in the thread and terminates it (so that a later "thread-join!" can pick it up), but whether that exception-handler unwinds dynamic-wind thunks isn't clear to me. I'll ponder about this. cheers, felix ___ Chicken-users mailing list Chicken-users@nongnu.org http://lists.nongnu.org/mailman/listinfo/chicken-users