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.. |
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.. > > |
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.. >> >> > > |
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: 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 | ... ]. |
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 | ... ]. the error nor the performance of the code. Levente > > > |
On Tue, Sep 6, 2016 at 1:02 AM, Levente Uzonyi <[hidden email]> wrote:
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 - |
Free forum by Nabble | Edit this page |