order of ensure: processing vs. error handling

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

order of ensure: processing vs. error handling

Chris Muller-4
Hi all, I have some in this code in which I try to read a file and, if
it fails, try to read it in a different way.

But the order of processing ensure: blocks vs. error-handler blocks is
not what I expected.  I expected the ensure: block of the inner
handler to run before the outer handler.

Here's a simple workspace to demonstrate. The temp var "open"
represents the status of a FileStream.

|open| open:=nil.
World findATranscript: nil  Transcript clear.
[ [ [ "try method1"  open:=true.  Error signal] ensure: [ open:=false] ]
    on: Error
    do:
        [ : innerErr |   "try method2, but file still open!"
        Transcript cr; show: 'innerErr handler, open is ', open asString.
        innerErr return:
            ([ open:=true.
            Error signal ] ensure: [ open:=false ]) ] ]

    on: Error
    do: [ : outerErr | Transcript cr; show: 'outerErr handler, open is
', open asString ].
Transcript cr; show: 'Done.  open is ', open asString.

It seems none of the ensure blocks are processed until after ALL the
error-handler blocks.  So if a #fileNamed:do: deep deep in the stack
encounters an Error, the file is still open in the upper-level
handlers, with no good way to close it..

Reply | Threaded
Open this post in threaded view
|

Re: order of ensure: processing vs. error handling

Andres Valloud-4
I'd think the ensure blocks haven't run yet because the return: messages
sent to the exceptions haven't finished, so exception handling hasn't
had a chance to unwind the stack yet.  If I understand things right,
what you are seeing is the expected behavior --- because e.g. nobody
knows whether an exception handler might fix something (or retry, or...)
and resume past the exception instead of aborting the block.

On 9/5/16 12:30 , Chris Muller wrote:

> Hi all, I have some in this code in which I try to read a file and, if
> it fails, try to read it in a different way.
>
> But the order of processing ensure: blocks vs. error-handler blocks is
> not what I expected.  I expected the ensure: block of the inner
> handler to run before the outer handler.
>
> Here's a simple workspace to demonstrate. The temp var "open"
> represents the status of a FileStream.
>
> |open| open:=nil.
> World findATranscript: nil  Transcript clear.
> [ [ [ "try method1"  open:=true.  Error signal] ensure: [ open:=false] ]
>     on: Error
>     do:
>         [ : innerErr |   "try method2, but file still open!"
>         Transcript cr; show: 'innerErr handler, open is ', open asString.
>         innerErr return:
>             ([ open:=true.
>             Error signal ] ensure: [ open:=false ]) ] ]
>
>     on: Error
>     do: [ : outerErr | Transcript cr; show: 'outerErr handler, open is
> ', open asString ].
> Transcript cr; show: 'Done.  open is ', open asString.
>
> It seems none of the ensure blocks are processed until after ALL the
> error-handler blocks.  So if a #fileNamed:do: deep deep in the stack
> encounters an Error, the file is still open in the upper-level
> handlers, with no good way to close it..
>
>

Reply | Threaded
Open this post in threaded view
|

Re: order of ensure: processing vs. error handling

Levente Uzonyi
Exactly. The error handler will add a new frame on top of the stack to
handle the error, but #ensure: will only evaluate its argument block when
its receiver block has been executed.

When the error occurs, #ensure:'s receiver block's frame will be on the
stack, somewhere below the error handler block's frame, hence #ensure:'s
argument block's evaluation will not have been started at that time.

@Chris: If I were you, I wouldn't bother with such magic. Instead, I'd use
a linear approach:

| isTheFileOpen |
isTheFileOpen := false.
[ try the first method. isTheFileOpen := true ] ifError: [].
isTheFileOpen ifFalse: [
  [ try the second method. isTheFileOpen := true ] ifError: [].
  isTheFileOpen ifFalse: [
  [ try the third method. isTheFileOpen := true ] ifError: [].
  ... ] ].

Levente

On Mon, 5 Sep 2016, Andres Valloud wrote:

> I'd think the ensure blocks haven't run yet because the return: messages sent
> to the exceptions haven't finished, so exception handling hasn't had a chance
> to unwind the stack yet.  If I understand things right, what you are seeing
> is the expected behavior --- because e.g. nobody knows whether an exception
> handler might fix something (or retry, or...) and resume past the exception
> instead of aborting the block.
>
> On 9/5/16 12:30 , Chris Muller wrote:
>> Hi all, I have some in this code in which I try to read a file and, if
>> it fails, try to read it in a different way.
>>
>> But the order of processing ensure: blocks vs. error-handler blocks is
>> not what I expected.  I expected the ensure: block of the inner
>> handler to run before the outer handler.
>>
>> Here's a simple workspace to demonstrate. The temp var "open"
>> represents the status of a FileStream.
>>
>> |open| open:=nil.
>> World findATranscript: nil  Transcript clear.
>> [ [ [ "try method1"  open:=true.  Error signal] ensure: [ open:=false] ]
>>     on: Error
>>     do:
>>         [ : innerErr |   "try method2, but file still open!"
>>         Transcript cr; show: 'innerErr handler, open is ', open asString.
>>         innerErr return:
>>             ([ open:=true.
>>             Error signal ] ensure: [ open:=false ]) ] ]
>>
>>     on: Error
>>     do: [ : outerErr | Transcript cr; show: 'outerErr handler, open is
>> ', open asString ].
>> Transcript cr; show: 'Done.  open is ', open asString.
>>
>> It seems none of the ensure blocks are processed until after ALL the
>> error-handler blocks.  So if a #fileNamed:do: deep deep in the stack
>> encounters an Error, the file is still open in the upper-level
>> handlers, with no good way to close it..
>>
>>
>
>

Reply | Threaded
Open this post in threaded view
|

Re: order of ensure: processing vs. error handling

Chris Muller-3
Thank you.  That makes perfect sense, when the Error is signaled, it must enter into the handler *deeper* in the stack (not unwound), and so of course its still IN the first part of the #ensure: method (valuing itself).  Got it.

@Chris: If I were you, I wouldn't bother with such magic. Instead, I'd use a linear approach:

| isTheFileOpen |
isTheFileOpen := false.
[ try the first method. isTheFileOpen := true ] ifError: [].
isTheFileOpen ifFalse: [
        [ try the second method. isTheFileOpen := true ] ifError: [].
        isTheFileOpen ifFalse: [
                [ try the third method. isTheFileOpen := true ] ifError: [].
                ... ] ].

Yes, I'll have to go sequential.

PS -- I don't care to use ifError: because it doesn't give me access to the Exception object, only its description and receiver.  I think it should more closely mirror on: Error do: [ :err | ... ].



Reply | Threaded
Open this post in threaded view
|

Re: order of ensure: processing vs. error handling

Levente Uzonyi
On Mon, 5 Sep 2016, Chris Muller wrote:

> Thank you.  That makes perfect sense, when the Error is signaled, it must enter into the handler *deeper* in the stack (not unwound), and so of course its still IN the first part of the #ensure: method
> (valuing itself).  Got it.
>       @Chris: If I were you, I wouldn't bother with such magic. Instead, I'd use a linear approach:
>
>       | isTheFileOpen |
>       isTheFileOpen := false.
>       [ try the first method. isTheFileOpen := true ] ifError: [].
>       isTheFileOpen ifFalse: [
>               [ try the second method. isTheFileOpen := true ] ifError: [].
>               isTheFileOpen ifFalse: [
>                       [ try the third method. isTheFileOpen := true ] ifError: [].
>                       ... ] ].
>
>
> Yes, I'll have to go sequential.
>
> PS -- I don't care to use ifError: because it doesn't give me access to the Exception object, only its description and receiver.  I think it should more closely mirror on: Error do: [ :err | ... ].
It's too late to change it. It's still useful when you don't care about
the error nor the performance of the code.

Levente

>
>
>

Reply | Threaded
Open this post in threaded view
|

Re: order of ensure: processing vs. error handling

Bert Freudenberg
On Tue, Sep 6, 2016 at 1:02 AM, Levente Uzonyi <[hidden email]> wrote:
On Mon, 5 Sep 2016, Chris Muller wrote:

PS -- I don't care to use ifError: because it doesn't give me access to the Exception object, only its description and receiver.  I think it should more closely mirror on: Error do: [ :err | ... ].

It's too late to change it. It's still useful when you don't care about the error nor the performance of the code.

That's because ifError: is from Smalltalk 80 and predates Exceptions. The old implementation is actually quite amazing in its simplicity. See implementors of #error: and #ifError: in an old Squeak image, e.g. http://try.squeak.org/#zip=http://files.squeak.org/1.1/Squeak1.1.zip

- Bert -