Error handlers and critical sections

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
6 messages Options
Reply | Threaded
Open this post in threaded view
|

Error handlers and critical sections

cdavidshaffer
One challenge I've noticed in several of my recent apps is dealing with
ensure: and critical: and friends in cases where Seaside's session error
handler deals with an error.  (BTW, this is conceptually different than
the case where we want to avoid call: inside such blocks.)  One has
domain code called from a Seaside application and the domain code
signals an otherwise unhandled exception.  These can be deep in the
domain model where one shouldn't be conscious of Seaside at all.  
Unfortunately the converse is true as well, there is no way for UI level
code to know of a protected block deep in the model.  What I'd like is a
trick to write a Seaside exception handler that conceptually looks like:

handleError: anError
    anError unwindTo: the place where the session installed an error
handler.
    display a document

I see an implementation of Exception>>unwindTo: but no senders or tests
so I'm not sure it even works (or does what I think it does).

Of course, in response to this, many Seasiders already use
critical:ifError: inside their model code but this ruins debugging.  The
rough equivalent for ensure: is:

BlockContext>>reallyEnsure: aBlock
    self on: Error do: [:ex | aBlock value. ex pass]

I generally don't worry about non Error-type exceptions but these could
be dealt with here as well.  Of course you can't retry, return etc. on
the notification once you've finalized your resources.  Exceptions: with
great power comes great responsibility ;-)

reallyEnsure:, like critical:ifError:, makes debugging a challenge since
whatever resources are being freed by aBlock are needed if we want to
raise a debugger.  In short the current behavior works great for
debugging but in production leaves resources "unfinalized" whereas my
solutions ruin debugging but make sure that finalization happens.

Anyone found a better solution?

David

_______________________________________________
Seaside mailing list
[hidden email]
http://lists.squeakfoundation.org/cgi-bin/mailman/listinfo/seaside
Reply | Threaded
Open this post in threaded view
|

Re: Error handlers and critical sections

cdavidshaffer
C. David Shaffer wrote:
> [snip]
>
> I see an implementation of Exception>>unwindTo: but no senders or
> tests so I'm not sure it even works (or does what I think it does).
>
Oops, that should be ContextPart>>unwindTo:  emphasizing even more that
I have no idea what I'm talking about ;-)

David

_______________________________________________
Seaside mailing list
[hidden email]
http://lists.squeakfoundation.org/cgi-bin/mailman/listinfo/seaside
Reply | Threaded
Open this post in threaded view
|

Re: Error handlers and critical sections

cdavidshaffer
In reply to this post by cdavidshaffer
In continuing the process of answering myself...

I tried:


[   [1/0] ensure: [Transcript show: 'got it.'; cr].
    Transcript show: 'should not get here.'; cr ]
        on: Error
        do: [:ex | |signalContext handlerContext|
            signalContext := ex instVarNamed: #signalContext.
            handlerContext := ex instVarNamed: #handlerContext.
            signalContext unwindTo: handlerContext.
            self halt]


Seems to work as expected ('got it' shows up in transcript before one
quits the debugger).  So, I'm trying:

ProductionErrorHandler>>handleError: ex
    |signalContext handlerContext|
    signalContext := ex instVarNamed: #signalContext.
    handlerContext := ex instVarNamed: #handlerContext.
    signalContext unwindTo: handlerContext.
     WACurrentSession value returnResponse: (WAResponse document: self
document mimeType: 'text/html')


for a bit to see how it goes.   If it seems right I'll embed it in
exception and avoid all the instVarNamed: stuff.  This makes ensure: and
critical: release their resources since there is no hope of re-entering
their contexts.

David

_______________________________________________
Seaside mailing list
[hidden email]
http://lists.squeakfoundation.org/cgi-bin/mailman/listinfo/seaside
Reply | Threaded
Open this post in threaded view
|

RE: Error handlers and critical sections

Ron Teitelbaum
Hi David,

I'm not sure I really understand your question.  There is a place in the
code for handling exceptions.  If you make your own subclass of WASession
then you can implement something like this:

withErrorHandler: aBlock
        "override and extend the basic seaside exception handling"

        ^[[super withErrorHandler: [aBlock
                on: USMRSDBObjectRegister do: [:ex | self registerObject: ex
object. ex resume]]]
                on: USMRSDBProxyReifier do: [:ex | self getObject: ex
object. ex resume]]
                on: USMRSDBCurrentCommitManager do: [:ex | ex resume: self
currentCommitManager].
       

The idea here is that the system will use the error handlers that are
included with seaside by calling super withErrorHandler: [

But then you can include your own Errors to handle.  Just subclass Exception
to create your own exception and name it what ever you want.  Then when you
want to handle a special case, raise the exception from your code.

For example in my code I could do this
       
        aCommitManager := USMRSDBCurrentCommitManager signal.

This raises an exception unwinds the stack to this handler and then returns
the currentCommitManager.

It all works beautifully.  Hope that helps!!

Happy Coding,

Ron Teitelbaum
President / Principal Software Engineer
US Medical Record Specialists
[hidden email]




> -----Original Message-----
> From: C. David Shaffer
>
> In continuing the process of answering myself...
>
> I tried:
>
>
> [   [1/0] ensure: [Transcript show: 'got it.'; cr].
>     Transcript show: 'should not get here.'; cr ]
>         on: Error
>         do: [:ex | |signalContext handlerContext|
>             signalContext := ex instVarNamed: #signalContext.
>             handlerContext := ex instVarNamed: #handlerContext.
>             signalContext unwindTo: handlerContext.
>             self halt]
>
>
> Seems to work as expected ('got it' shows up in transcript before one
> quits the debugger).  So, I'm trying:
>
> ProductionErrorHandler>>handleError: ex
>     |signalContext handlerContext|
>     signalContext := ex instVarNamed: #signalContext.
>     handlerContext := ex instVarNamed: #handlerContext.
>     signalContext unwindTo: handlerContext.
>      WACurrentSession value returnResponse: (WAResponse document: self
> document mimeType: 'text/html')
>
>
> for a bit to see how it goes.   If it seems right I'll embed it in
> exception and avoid all the instVarNamed: stuff.  This makes ensure: and
> critical: release their resources since there is no hope of re-entering
> their contexts.
>
> David
>
> _______________________________________________
> Seaside mailing list
> [hidden email]
> http://lists.squeakfoundation.org/cgi-bin/mailman/listinfo/seaside

_______________________________________________
Seaside mailing list
[hidden email]
http://lists.squeakfoundation.org/cgi-bin/mailman/listinfo/seaside
Reply | Threaded
Open this post in threaded view
|

Re: Error handlers and critical sections

cdavidshaffer
Ron Teitelbaum wrote:

> Hi David,
>
> I'm not sure I really understand your question.  There is a place in the
> code for handling exceptions.  If you make your own subclass of WASession
> then you can implement something like this:
>
> withErrorHandler: aBlock
> "override and extend the basic seaside exception handling"
>
> ^[[super withErrorHandler: [aBlock
> on: USMRSDBObjectRegister do: [:ex | self registerObject: ex
> object. ex resume]]]
> on: USMRSDBProxyReifier do: [:ex | self getObject: ex
> object. ex resume]]
> on: USMRSDBCurrentCommitManager do: [:ex | ex resume: self
> currentCommitManager].
>
>
>  
Yes!  I think that would be better...but with some minor differences.  
All of your handlers jump back into the signaling context (they are more
like Notifications).  There's no unwinding to be done.  I'm talking
about dealing with unexpected Errors.  Let me spell out an example:

MyComponent>>someCallbackMethod
    model doSomethingImportant

now in my model

MyModel>>doSomethingImportant
    [self useSomeResource. UnexpectedError signal] ensure: [self
freeSomeResource]

This ensure: block will never run unless UnexpectedError is handled and
that handler block exits.  This never happens if you use subclasses of
WAErrorHandler to deal with errors...hence my unwindTo: attempt.   Your
example led me to:

MySession>>withErrorHandler: aBlock
    ^super withErrorHandler: [aBlock on: Error do: [:ex | MyErrorLogger
logError: ex].
            self returnResponse: ...]

Notice that in order to satisfy the requirement that the resource be
freed the "self returnResponse:" must be outside the error handler so
that the ensure: blocks gets invoked.  I think this is much better than
my previous approach.  Thanks Ron!

David

_______________________________________________
Seaside mailing list
[hidden email]
http://lists.squeakfoundation.org/cgi-bin/mailman/listinfo/seaside
Reply | Threaded
Open this post in threaded view
|

RE: Error handlers and critical sections

Ron Teitelbaum
You are welcome!

- Ron

> -----Original Message-----
> From: C. David Shaffer
>
> Ron Teitelbaum wrote:
> > Hi David,
> >
> > I'm not sure I really understand your question.  There is a place in the
> > code for handling exceptions.  If you make your own subclass of
> WASession
> > then you can implement something like this:
> >
> > withErrorHandler: aBlock
> > "override and extend the basic seaside exception handling"
> >
> > ^[[super withErrorHandler: [aBlock
> > on: USMRSDBObjectRegister do: [:ex | self registerObject: ex
> > object. ex resume]]]
> > on: USMRSDBProxyReifier do: [:ex | self getObject: ex
> > object. ex resume]]
> > on: USMRSDBCurrentCommitManager do: [:ex | ex resume: self
> > currentCommitManager].
> >
> >
> >
> Yes!  I think that would be better...but with some minor differences.
> All of your handlers jump back into the signaling context (they are more
> like Notifications).  There's no unwinding to be done.  I'm talking
> about dealing with unexpected Errors.  Let me spell out an example:
>
> MyComponent>>someCallbackMethod
>     model doSomethingImportant
>
> now in my model
>
> MyModel>>doSomethingImportant
>     [self useSomeResource. UnexpectedError signal] ensure: [self
> freeSomeResource]
>
> This ensure: block will never run unless UnexpectedError is handled and
> that handler block exits.  This never happens if you use subclasses of
> WAErrorHandler to deal with errors...hence my unwindTo: attempt.   Your
> example led me to:
>
> MySession>>withErrorHandler: aBlock
>     ^super withErrorHandler: [aBlock on: Error do: [:ex | MyErrorLogger
> logError: ex].
>             self returnResponse: ...]
>
> Notice that in order to satisfy the requirement that the resource be
> freed the "self returnResponse:" must be outside the error handler so
> that the ensure: blocks gets invoked.  I think this is much better than
> my previous approach.  Thanks Ron!
>
> David
>
> _______________________________________________
> Seaside mailing list
> [hidden email]
> http://lists.squeakfoundation.org/cgi-bin/mailman/listinfo/seaside

_______________________________________________
Seaside mailing list
[hidden email]
http://lists.squeakfoundation.org/cgi-bin/mailman/listinfo/seaside