Re: [Chicken-users] Threads and dynamic-wind still problematic

2009-03-10 Thread Tobia Conforto

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

2009-03-10 Thread Peter Bex
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

2009-03-10 Thread Jim Ursetto
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

2009-03-10 Thread felix winkelmann
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

2009-03-09 Thread John Cowan
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

2009-03-09 Thread Tobia Conforto

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

2009-03-09 Thread Tobia Conforto

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

2009-03-09 Thread Tobia Conforto

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

2009-03-09 Thread felix winkelmann
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

2009-03-08 Thread Peter Bex
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

2009-03-07 Thread felix winkelmann
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

2009-03-05 Thread felix winkelmann
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


[Chicken-users] Threads and dynamic-wind still problematic

2009-03-02 Thread Peter Bex
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:

$ csi
#;1> (use srfi-18)
; loading library srfi-18 ...
#;2> (dynamic-wind (lambda () (print "BEFORE")) (lambda () (error "die!")) 
(lambda () (print "AFTER")))
BEFORE
Error: die!

Call history:

(begin (error "die!"))
(error "die!")
(lambda () (print "AFTER"))
(begin (print "AFTER"))
(print "AFTER")
  (dynamic-wind (lambda () (print "BEFORE")) (lambda () 
(error "die!")) (lambda () (print "AFTER")))
  (print "BEFORE")
  (error "die!")  <--
AFTER
#;2> (thread-start! (lambda () (dynamic-wind (lambda () (print "BEFORE")) 
(lambda () (error "die!")) (lambda () (print "AFTER")
#
#;3> BEFORE
Warning (#): : die!

Call history:

  (dynamic-wind (lambda () (print "BEFORE")) (lambda () 
(error "die!")) (lambda () (print "AFTER")))
  (print "BEFORE")
  (error "die!")  <--

I'd say this is *extremely* counterintuitive; that raising an error
outside a thread (or rather; in the primordial thread) causes pending
dynamic-unwinds to unwind while doing the same in a thread does not.
In fact, I'd say it's outright dangerous; this can cause hard to track
race condition type bugs.

This bug is still there in Chicken 4.

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


pgp2k4Zow7nSv.pgp
Description: PGP signature
___
Chicken-users mailing list
Chicken-users@nongnu.org
http://lists.nongnu.org/mailman/listinfo/chicken-users