[gambit-list] Your feedback would be much appreciated re: Proposal for enabling IO errors to be reported through returning of a custom value instead of by throwing exception, through DSL with exports: ##io-error-behavior param, ##default-io-error-behavior unique value, ##last-io-error param.

Mikael mikael.rcv at gmail.com
Fri Apr 5 16:29:23 EDT 2013


Great news! :-D

Thanks!

2013/4/4 Marc Feeley <feeley at iro.umontreal.ca>

> I've added a port-specific exception handler that can be set with the new
> "port-io-exception-handler-set!" procedure. I/O exceptions which are
> detected by the I/O primitives are passed to this handler (if one is set)
> by calling it in tail position with respect to the primitive.  That way,
> you can define the specific behavior you need in your application.  In
> particular, if the handler returns a value (instead of raising an
> exception), then that value will be the result of the primitive.
>
> Here's an example:
>
> (let ((p (open-tcp-client "localhost:9999")))
>   (port-io-exception-handler-set!
>    p
>    (lambda (e) (display-exception e (current-output-port)) #f))
>   (pp (list 'return-value= (read-u8 p))))
>
> that prints:
>
> Connection refused
> (read-u8 '#<input-output-port #4 (tcp-client "localhost" 9999)>)
> (return-value= #f)
>
> Note that timeouts are not considered to be io-exceptions.  That shouldn't
> be a problem because a timeout handler can be set explicitly for any port
> in addition to the io-exception handler.
>
> Marc
>
> On 2013-03-24, at 2:12 PM, Mikael <mikael.rcv at gmail.com> wrote:
>
> > Hi Marc!
> >
> > Ah, one thing struck me: in some cases, IO primitives don't raise an
> exception on error but instead return a special value, I think it's #!eof
> always.
> >
> > This would not transform to returning #f by a with-exception-handler
> wrapper, but it could with a port specific flag |io-error| or |eof-value|
> or |treat-eof-as-error|.
> >
> >
> >
> > Do you have any thoughts on the previous email on with-exception-handler
> and IO error semantics yet?
> >
> > Of course there's time though would be great to get this question about
> how to reliably do general IO error handling settled.
> >
> >
> > The two possible ways I have the impression that there are now are
> >
> > 1) By:
> >  * Add a |treat-eof-as-error| port flag
> >  * Add an optional third arg |first?| to |with-exception-catcher| and
> -handler that makes the thunk be invoked first in case of exception.
> >    Internally there's two exception handler chains, the non-first that
> works just like the one today - an exception handler added makes it be
> invoked as first line in case of exception, and another chain that has
> priority over the non-first chain, that's invoked before it and where new
> handlers are added at the end and not at the beginning. (Perhaps internally
> they can be implemented as one chain only.)
> >  * Make |with-exception-handler| exception handlers recursive (now they
> make an infinite loop).
> >
> > or
> >
> > 2): (Less general solution, so probably not desirable) By:
> >  * Add a port-specific flag |io-error| to IO ports that's invoked in the
> current place of both |raise| and eof.
> >
> > In either of these two, the IO primitives need to be checked so that the
> IO error handling/|raise| calls are done in tail position / place that
> produces the primitive's return value anyhow.
> >
> > Thanks and best regards,
> > Mikael
> >
> > 2013/3/22 Mikael <mikael.rcv at gmail.com>
> > Dear Marc,
> >
> > Spontaneously I think that the prospect of using with-exception-handler
> as you propose sounds better than adding custom error behavior code to the
> IO, as, the w.e.h. route would be more holistic in that it maintains the
> error handling mechanism in Gambit one in total in number; also,
> >
> > It would work from a performance point of view as there is no overhead
> per IO primitive call that's successful, which accounts for almost all of
> them. The only potential issue with performance would be if the exception
> handler part would take a lot of time, though I guess it can be generalized
> that that is not an issue.
> >
> > Then I guess the last point would be the design aspect that if somehow
> IO code would be run outside the current environment that the w.e.h. is
> installed in, the custom error behavior would disappear; that would indeed
> be a benefit with a port specific flag, that it's not subject to the same
> limitation. Though, from the practical use I see today, that more or less
> does not happen so it's fine.
> >
> >
> > I didn't think deeper about the possibility of w.e.h. before as I found
> myself without clarity on how the things I addressed in the previous email
> could be solved, as they need to be solved for it to be a practically
> viable solution.
> >
> >
> > Looking forward a lot to hear your take on those two-three things and
> hopefully get to a reliable io-error => #f soon =)
> >
> > Thanks,
> > Mikael
> >
> >
> >
> > 2013/3/20 Mikael
> >
> > Hi Marc!
> >
> >
> > 2013/3/18 Marc Feeley <feeley at iro.umontreal.ca>
> > The problem I see with the approach you propose (whether it uses a
> parameter or a port specific flag) is that the semantics of an "io-error"
> is vague.  What is an IO error?  The definition is important because
> exceptions that are IO errors are going to be processed using this new
> mechanism, and non-IO exceptions will use a different mechanism (normal
> exception handling).
> >
> > The approach I propose does not have this problem because the programmer
> has complete control over the definition of an IO error.  The exception
> object can be inspected to see if it qualifies as an IO error and an
> appropriate action can be taken.  The definition of IO error can depend on
> the type of port, the type of primitive which caused the exception
> (read-u8, read-char, read), etc.
> >
> > Marc
> >
> >
> > Aha. To really get this, there are two quite fundamental things about
> the applicability of the with-exception-handler possibility that I maybe
> don't get yet or at least would benefit of clarification, can we have a
> look at it?
> >
> > Also last a question re semantics of io-error.
> >
> >
> >
> > So, for the with-exception-handler mechanism to work out, it needs to go
> together with other use of exception handling use that's being done in the
> same scope.
> >
> > In a setup where there's another exception handler *outside* the IO
> with-exception-handler, there would be no issue as the IO w.e.h. 's handler
> would be invoked first for any exception that occurs, and do its matching
> and handling.
> >
> > In a setup where the other exception handler is made *inside* the IO
> w.e.h. though, any IO exception that arises within that exception handler
> will be picked up by it first, and for the IO w.e.h. thing to work out,
> that handler needs to be able to pass on the exception to the parent
> exception handler (which is the IO w.e.h.) completely in its original
> shape, in such a way that if the IO w.e.h. handler returns a value, that
> will be passed as return value to the original |raise| call.
> >
> > An example of this would be a web-based PI calculator that uses
> exception handling locally in its PI calculate request thunk to pick up
> invalid user input.
> >
> > So let's ask how this could be done.
> >
> > Let's say below that we have a procedure (install-io-w.e.h. port thunk)
> that installs the IO w.e.h. that makes IO primitives return #f on
> exception, so that thunk is invoked with that w.e.h. installed.
> >
> > Then, we have application logics (logics) that, aside from using IO,
> internally uses exception handling.
> >
> > So a setup something like,
> >
> > (define (logics)
> >   (with-exception-catcher
> >     (lambda (e) "Logics failed due to invalid user input!")
> >     (lambda ()
> >       (pp (read-u8 broken-port))
> >       (/ 0 0) ; This is to simulate an exception due to invalid user
> input.
> >       )))
> > (install-io-w.e.h. broken-port logics)
> >
> > The desired behavior here is to print #f to the console and for logics
> to then return "Logics failed due to invalid user input!".
> >
> > The issue now becomes, how would this local exception handler need to be
> implemented as for this to work out.
> >
> >
> > The local exception handler needs to be specific about what kind of
> error it looks for as to know which to handle locally and which to
> re-raise. This might be a complete PITA in some situations as you're
> looking for a catch-all behavior, as in the example above!
> >
> > Perhaps the order of exception handlers could be tweaked somehow, so
> that the IO w.e.h. would get highest priority or something, though how
> could that be made as a 'clean' abstraction? I mean, who's in a place to
> claim at a general level that one exception is of higher prio than another?
> - different classes could be introduced, like, "user exceptions" and "io
> exceptions" or "system exception" (this would lead to an at least almost
> functional equivalent of the port specific flag solution, just with a more
> indirect code path!), or, the exception matching procedure could be
> exported to a separate mechanism, and the exception handler claiming the
> highest specificity in the matching would get to handle it e.g.
> (with-exception-handler exception-match exception-handler thunk) where
> exception-match takes an exception argument e and returns how well it
> matches the exception, i duno as a boolean or 0-10 or a symbol.
> >
> > Or, a global parameter object |is-nonlocal-exception?| could be
> introduced that any exception handler is free to invoke as to check if it
> should re-raise the exception, and to overlap.. hmm, a more general
> solution would be better.
> >
> > Do you see any general solution to this specificity problem?
> >
> >
> >
> >
> >
> >
> > Now to get to the next thing, let's just presume there's a solution to
> this and we represent it in this example as a (if (is-nonlocal-exception?
> e) (raise e) condition in the local exception handler, again not because
> this would necessarily represent a general solution but just to get on to
> the next thing in the reasoning; so now we have
> >
> > (define (logics)
> >   (with-exception-catcher
> >     (lambda (e) (if (is-nonlocal-exception? e) (raise e) "Logics failed
> due to invalid user input!"))
> >     (lambda ()
> >       (pp (read-u8 broken-port))
> >       (/ 0 0) ; This is to simulate an exception due to invalid user
> input.
> >       )))
> > (install-io-w.e.h. broken-port logics)
> >
> >
> > Now, how do you make this *parent* exception handler (the IO w.e.h.)
> that got the exception passed to it, able to pass a return value to the
> original |raise| call?
> >
> > This is required for the exception handling-based IO error handling
> behavior we're looking for to work.
> >
> > The problem reduces to
> >
> > (define (logics) (raise "Please return 'properly-handled!"))
> >
> > (define (proxy-exception-handler/catcher2 thunk) (lambda ()
> (with-exception-catcher (lambda (e) (raise e)) thunk)))
> >
> > (define (proxy-exception-handler/catcher1 thunk) (lambda ()
> (with-exception-handler (lambda (e) (raise e)) thunk)))
> >
> > (define (parent-exception-handler thunk) (continuation-capture (lambda
> (cont) (with-exception-handler (lambda (e) 'properly-handled!) thunk))))
> >
> > where proxy-exception-handler/catcher 1 & 2 are to represent an
> arbitrary chain of exception handlers that logics code may come up with.
> The intended behavior is
> >
> > (parent-exception-handler (proxy-exception-handler/catcher1
> (proxy-exception-handler/catcher2 logics))) => 'properly-handled!
> >
> > (parent-exception-handler (proxy-exception-handler/catcher1 logics)) =>
> 'properly-handled!
> >
> > (parent-exception-handler (proxy-exception-handler/catcher2 logics)) =>
> 'properly-handled!
> >
> >
> > Actually evaluating these three tests showed:
> >
> > (parent-exception-handler (proxy-exception-handler/catcher1
> (proxy-exception-handler/catcher2 logics))) => infinite loop (!)
> >
> > (parent-exception-handler (proxy-exception-handler/catcher1 logics)) =>
> infinite loop (!)
> >
> > (parent-exception-handler (proxy-exception-handler/catcher2 logics)) =>
> 'properly-handled!
> >
> > To start with, great that we see that with-exception-catcher delivers
> out of the box for this usecase!
> >
> > Thus we have with-exception-handler left. I guess the best way would be
> if with-exception-handler inherently somehow would deliver for this, so
> that support for this kind of use would be transparent and not require
> possible updates of user code (e.g. any typical user code such as a PI
> calculator etc. could without needing code review just be run within a web
> server that uses this special IO error handling).
> >
> > Is there any way to make with-exception-handler deliver for this usecase?
> >
> >
> >
> >
> >
> >
> > Last, what about that for introducing a port specific flag there would
> be the issue that the semantics of an IO error is vague -
> >
> > Maybe you see something here I didn't get. As IO error for a port would
> count any error reported from the OS about the port, as well as timeouts
> within Gambit's IO system.
> >
> > So this would correspond to any OS IO primitive invocation that gives an
> error return value (other than one that asks for a reiteration of the
> procedure, which some of them come with).
> >
> > Another way to relate to it would be that such a port specific flag
> would serve to protect from requirement of programmer or admin intervention
> for any IO error that could possibly come up regarding the addressed ports.
> >
> > What do you see here?
> >
> >
> >
> > Best regards,
> > Mikael
> >
> >
> >
> >
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mailman.iro.umontreal.ca/pipermail/gambit-list/attachments/20130405/a806f577/attachment.htm>


More information about the Gambit-list mailing list