[Cuis-dev] Unwind mechanism during termination is broken and inconsistent

Juan Vuletich juan at jvuletich.org
Fri Jun 4 06:43:24 PDT 2021


Hi Jaromir,

I've just pushed your latest to GitHub. I included a note with links to 
this email thread in #terminate. I also added the new tests; and your 
examples as a class method in Process, for future reference.

This is an important piece of work. Thanks a lot!

Cheers,

-- 
Juan Vuletich
www.cuis-smalltalk.org
https://github.com/Cuis-Smalltalk/Cuis-Smalltalk-Dev
https://github.com/jvuletich
https://www.linkedin.com/in/juan-vuletich-75611b3
@JuanVuletich


On 6/1/2021 9:09 AM, Jaromir Matas via Cuis-dev wrote:
>
> Hi Juan,
>
> I’m enclosing a finalized version of #terminate with the following 
> changes:
>
> 1) whole termination code wrapped in #valueEnsured as suggested in the 
> previous message
>
> 2) changed approach to BlockCannotReturn: error and adopted Christoph 
> Thiede’s solution allowing the debugger to Proceed from the error to 
> the sender of the failing non-local return. It makes sense in the 
> debugger to leave all choices open for the user – Proceeding means 
> ignoring the failed non-local return but why not - it could have been 
> a typo after all :) So I’m taking back my suggestion to disable the 
> Proceed button during unwinding – too restrictive. This change allows 
> for debugging examples like:
>
> [^2] fork
>
> Or even:
>
> [[self error] ensure: [^2]] fork
>
> Previously these would crash the image.
>
> To make this work with #terminate and notify the user I added a 
> ProceedBlockCannotReturn warning.
>
> 3) #complete:to: is a helper method to improve readability of 
> #terminte and also to deal with two special exceptions: 
> BlockCannotReturn and MessageNotUnderstood. The latter exception was 
> purposely written to allow for writing methods while debugging which 
> introduced a potential for an infinite recursion I had to deal with 
> (see notes in #complete:to: )
>
> 4) I’ve also integrated a fix to a stepOver bug in this example:
>
> [^2] ensure: [Transcript show: 'done']
>
> If you step through and then step over ^2 you get a  BlockCannotReturn 
> error – that’s a bug
>
> The problem is #return:from: supplies the first unwind context to 
> #aboutToReturn:through: but this first unwind context was determined 
> before the debugger inserted an #ensure context during the stepover 
> simulation. My fix is to supply nil and let #resume:through: find the 
> first unwind context at the right moment.
>
> This fix modifies #resume:through: and #return:from: methods only and 
>  is independent of the #teminate suite.
>
> Please let me know if you find any inconsistencies… I hope not ;)
>
> My best regards,
>
> Jaromir
>
> *From: *Jaromir Matas via Cuis-dev <mailto:cuis-dev at lists.cuis.st>
> *Sent: *Tuesday, May 25, 2021 9:32
> *To: *Juan Vuletich <mailto:juan at jvuletich.org>; 
> cuis-dev at lists.cuis.st <mailto:cuis-dev at lists.cuis.st>
> *Cc: *Jaromir Matas <mailto:mail at jaromir.net>
> *Subject: *Re: [Cuis-dev] Unwind mechanism during termination is 
> broken and inconsistent
>
> Hi Juan,
>
> I’ve explored a scenario when you are terminating a process, i.e. 
> unwinding it’s ensure etc contexts and in the middle of it the process 
> running the unwinds gets interrupted and terminated. In such case this 
> process unwinds its stack and terminates but the original process is 
> just left hanging as a suspended process (potentially indefinitely) or 
> as a chain of contexts that get garbage collected eventually.
>
> With the new terminate there’s an elegant solution to this scenario: 
> wrap the whole termination code in #valueEnsured and the intruding 
> process will correctly unwind both the original and the terminator 
> process :) I’ve enclosed a test and modified #terminate only. Please 
> take a look, I can’t see anything wrong with this approach.
>
> Best regards,
>
> Jaromir
>
> *From: *Jaromir Matas <mailto:mail at jaromir.net>
> *Sent: *Sunday, May 23, 2021 19:10
> *To: *Juan Vuletich <mailto:juan at jvuletich.org>; 
> cuis-dev at lists.cuis.st <mailto:cuis-dev at lists.cuis.st>
> *Subject: *FW: [Cuis-dev] Unwind mechanism during termination is 
> broken and inconsistent
>
> Hi Juan,
>
> I'm enclosing a test covering a situation where the top context itself 
> is an unwind context. The test explores three scenarios based on the 
> position where the execution was suspended: before entering the unwind 
> block, right after setting complete := true but before executing the 
> unwind block and after returning from the unwind block. #terminate 
> should execute the unwind block in the first two cases but not in the 
> last one.
>
> Squeak has a convenient method Process>>#forBlock:runUntil: to step 
> right into the desired position. So I just inlined its code into the 
> test to make it work the same way :)
>
> Regarding the tests demonstrating the unwind behavior in case of 
> unhandled errors and subsequent Abandoning or Proceeding the debugger 
> - it looks like Squeak has a machinery to do that (debugger tests) but 
> I can't see anything similar in Cuis that would simulate opening and 
> abandoning a debugger... Any advice?
>
> Many thanks for all your feedback,
>
> Best regards,
>
> Jaromir
>
> PS: Yesterday my message bounced back due to it’s size, I’m forwarding 
> it again and trimming history.
>
> *From: *Jaromir Matas <mailto:mail at jaromir.net>
> *Sent: *Saturday, May 22, 2021 23:16
> *To: *Juan Vuletich <mailto:juan at jvuletich.org>; Discussion of Cuis 
> Smalltalk <mailto:cuis-dev at lists.cuis.st>
> *Subject: *RE: [Cuis-dev] Unwind mechanism during termination is 
> broken and inconsistent
>
> Hi Juan,
>
> Many thanks for your feedback.
>
> I've made some final changes to the changeset:
>
> 1) extracted a repeating code to a method called Process>>complete:to: 
> which calls #runUnwindUntilErrorOrReturnFrom: and deals with potential 
> infinite loops caused by some errors.
>
> 2) as a result the unwind code pattern used in #terminate is almost 
> identical to the one in #resume:through: - which I think is better for 
> readability - when people can recognize a pattern right away :)
>
> 3) adjusted the part searching for the outer-most unwind context to 
> catch even the top context if it's an unwind context.
>
> 4) In order to rein in the infinite loops or crashes caused by the 
> BlockCannotReturn error I added a local variable isRecursive to 
> BlockCannotReturn exception to be able to deal with a starting 
> infinite loop. This is an example brought to my attention Christoph 
> Thiede:
>
> ```
>
> "Both statements need to be executed separately in a Workspace"
>
> a := [true ifTrue: [^ 1] yourself]
>
> [a value] on: BlockCannotReturn do: [:ex | ex resume]
>
> ```
>
> With the proposed fix there's no need to change the isResumable 
> setting of the BlockCannotReturn I suggested previously. Resuming 
> doesn't hurt any longer :)
>
> Regarding my suggestion to disable the Proceed button when debugging 
> an error that happened during unwind - I'm no longer so sure it's a 
> good idea to limit the freedom of the user in such a border situation 
> like debugging an unwind error :) I suggest to leave it open, "use at 
> your own risk" type of thing, for the moment.
>
> > I think you are right in the previous email, and we'd integrate your 
> latest change.I have already checked that none of the tests we 
> recently added breaks because of it. If you could turn some of the 
> arguments you provide into tests, it would be great.
>
> I must admit I still haven't learned to build tests involving errors 
> etc. :) But at least I updated and enclosed the list of examples that 
> can be used as test scenarios and added some more extreme ones.
>
> Juan, please review the enclosed changeset and let me know if you find 
> something suspicious. Please feel free to change any names I picked or 
> do any changes you find useful.
>
> Best regards,
>
> Jaromir
>
> *From: *Juan Vuletich <mailto:juan at jvuletich.org>
> *Sent: *Friday, May 21, 2021 21:08
> *To: *Discussion of Cuis Smalltalk <mailto:cuis-dev at lists.cuis.st>
> *Cc: *Jaromir Matas <mailto:mail at jaromir.net>; Hernan Wilkinson 
> <mailto:hernan.wilkinson at 10pines.com>
> *Subject: *Re: [Cuis-dev] Unwind mechanism during termination is 
> broken and inconsistent
>
> Hi Jaromir,
>
> On 5/16/2021 5:26 PM, Jaromir Matas via Cuis-dev wrote:
>
>     Hi Juan,
>
>     I'm enclosing an updated changeset (please disregard the previous
>     one); I made a stupid assumption about
>     #runUntilErrorOrReturnFrom:. The new changeset leaves it untouched
>     and adds its replica called #runUnwindUntilErrorOrReturnFrom: with
>     the required functionality.
>
>
> I think you are right in the previous email, and we'd integrate your 
> latest change.I have already checked that none of the tests we 
> recently added breaks because of it. If you could turn some of the 
> arguments you provide into tests, it would be great.
>
>     I'd like to point out a little issue with the proposed semantics
>     of nested errors during termination:
>
>     Below’s a simple example. If you abandon the first debugger it
>     means you want to terminate the execution and unwind the
>     terminated process but an error happens during the unwind. You get
>     another debugger reporting the error and asking you what to do
>     next - to debug the error or proceed. But proceeding a termination
>     really means to proceed with the **unwind business only** and not
>     to proceed with the original computation. Here's my point: Hitting
>     Proceed will resume the original computation beyond the unwind
>     blocks but Abandon will only finish the unwind procedure which is
>     exactly what we want. In Squeak I simply disabled the Proceed
>     button but in Cuis I couldn't see such a simple solution so I left
>     it unresolved.
>
>     [self error: 'x1'] ensure: [
>
>         [self error: 'x2'] ensure: [
>
>             Transcript show: '2'].
>
>         Transcript show: '1'].
>
>     Transcript show: '0'
>
>     ---> you want to see '21' but proceeding the debugger will give
>     you '210' where '0' is printed outside of unwind blocks.
>
>     So just that you know there's still a little inconsistency in the
>     termination semantics.
>
>
> I see. The issue is how to disable a button so it doesn't act? The 
> simplest way is by saying `aButton lock: true`. We could add #enable 
> and #disable to PluggableButtonMorph (calling self lock: ). Or perhaps 
> call it #isEnabled: like in MenuItemMorph, although it is an ugly 
> name. Can you prepare a change set like this? That would be great. I 
> mean, I don't know where those calls should be made!
>
>     One more question: do you have an idea why BlockCannotReturn error
>     is considered resumable? I'd say it's unresumable by definition…?
>     Thanks :)
>
>
> Well, that method is pretty old, and in the mean time, exception 
> handling has gotten a lot better, thanks to several batches of 
> improvements, latter ones by you. So, please provide a patch to change 
> that, so it properly carries your initials.
>
>     best regards,
>
>     Jaromir
>
>
> Thanks again Jaromir for this amazing work.
>
> Cheers,
>

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.cuis.st/mailman/archives/cuis-dev/attachments/20210604/33592cf5/attachment-0001.htm>


More information about the Cuis-dev mailing list