Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it ok to execute the entire ensure block if the process is terminated?

Today I've learned that in Pharo the execution of:

[v := 1] ensure: [self halt. v := 2]

will end up setting v = 2, even when we abandon the process at the halt window(!).

I find this debatable. For me, the semantics of #ensure: means that the sequence

self halt. v := 2

must be executed, regardless the circumstances with the receiver block, not regardless the logic of the argument block. And since the logic of #halt includes the event of terminating the process, I find it intrusive the obstinate evaluation of the second sentence.

Next I tried the following:

[v := 1] ensure: [1 / 0. v := 2]

When the ZeroDivide exception popped-up I closed the debugger and still the value of v was 2 (same as with #halt.)

Finally, I evaluated:

[v := 1] ensure: [n := 1 / 0. v := v + n]

and closed the debugger on the ZeroDivide exception. This time the value of v was 1 but I got no exception from the fact that v + n cannot be evaluated. In other words, the error went on silently.

So my question is. What's the rational behind this behavior? Shouldn't the process just terminate at the point it would terminate under "normal" circumstances, i.e., with no #ensure: involved?

like image 995
Leandro Caniglia Avatar asked Sep 06 '15 01:09

Leandro Caniglia


2 Answers

I guess this is behavior which is not fully defined by any (ANSI) standard, but correct me, if I am wrong.

Other Smalltalks seem to behave different. I tried it in Smalltalk/X, where the Debugger offers 3 options: "Continue" (i.e. proceed), "Abort" (i.e. unwind) and "Terminate" (i.e. kill the process). I guess "Terminate" corresponds to what Squeak does when you close the debugger.

With "Abort" and "Terminate", the rest of the ensure block is NOT executed, with "Continue" it is. I guess that is ok, and what you would expect.

On Abort and Terminate (which are both unwinds to corrsponding exception handlers), it should not try to reevaluate or proceed the potentially wrong/bad/failing ensure block.

It is be the choice of the handler (which the Debugger basically is) if it wants to proceed or not. If not, then it should get out of the ensure block and continue to execute any other ensure blocks which may be above in the calling chain.

This is consistent with the behavior of exception handling blocks, which are also not reevaluated or proceeded if the same exception is raised within. In ST/X, there is explicit code in the exception classes which cares for this situation, so it is definitely by purpose and not by side effect.

My guess is that this is wrong in Squeak and the Squeak developers should be told.

like image 39
blabla999 Avatar answered Jan 01 '23 08:01

blabla999


Interesting one. It seems that your answer lies in the method BlockClosure>>valueNoContextSwitch, which is called by #ensure:. If you read the comment there, it says that it creates an exact copy of BlockClosure>>value (in a primitive), and the return value of that copy gets returned, not the return value of the original block containing your halt which you terminated. So the copy gets executed (apparently ignoring the copied halt), even if the original doesn't get to finish.

My guess is that this is intended to ensure (no pun intended) that the ensure: block always runs, but has the unintended side effect of ignoring the termination of the original block. I agree with you that this is not only counter-intuitive, but also probably not what was intended.

like image 97
Amos M. Carpenter Avatar answered Jan 01 '23 08:01

Amos M. Carpenter