Hi all, hi Jaromir,
I'm raising a new question in this post that is related to the following threads, but I think that it deserves its own thread due to the fundamental criticism expressed: [1, 2]
I just took a closer look at ProcessTest >> #testNestedUnwind and I have to say that I don't agree with it. I'm sorry that I did not mention this earlier, but somehow this aspect of Jaromir's large amount of recent work has escaped my attention before today. For reference, so that we all know what we are talking about, here is the test in question:
testNestedUnwind
"Test all nested unwind blocks are correctly unwound; all unwind blocks halfway through their execution should be completed or at least attempted to complete, not only the innermost one"
| p x1 x2 x3 |
x1 := x2 := x3 := false.
p :=
[
[
[ ] ensure: [ "halfway through completion when suspended"
[ ] ensure: [ "halfway through completion when suspended"
Processor activeProcess suspend.
x1 := true].
x2 := true]
] ensure: [ "not started yet when suspended"
x3 := true]
] fork.
Processor yield.
p terminate.
self assert: x1 & x2 & x3.
I'm not convinced about the assertions in this test. :-) In fact, I would only expect x3 to be true but x1 and x2 to be false! IMHO, when terminating a process, halfway-executed unwinded contexts should not be continued. Only not-yet-activated unwind contexts should be triggered. Here are my arguments:
* Regular unwinding and process termination should have exactly the same behavior.
Assume we manipulated the example from the test like this: [ [ [ [ ] ensure: [ "halfway through completion when suspended" [ ] ensure: [ "halfway through completion when suspended" self error. x1 := true]. x2 := true] ] ensure: [ "not started yet when suspended" x3 := true] ] on: Error do: [] ] fork. I have highlighted the differences, so what I changed was i) to insert an error handler at the bottom of the process and ii) instead of terminating the process, to raise an error in the innermost block. In this example, only x3 will be set to true which is because the exceptional control flow explicitly discontinues the logic running inside the error handler. Only not-yet-activated unwind contexts will be triggered as part of the unwinding, which only applies to the outermost unwind context. In my view, process termination should have exactly the same semantics as using an exception to abort the control flow. If we would not catch the error in the above example but press Abandon in the appearing debugger instead, I see no reason why we would want to execute a different set of unwind contexts.
* Last but not least, the fact that an error has been signaled means that the signalerContext is "infected" so under no circumstances, abandoning the process should resume the execution of this infected context! (The only exception is when you consciously do so via the "Proceed" button in a debugger.) This might become more vivid if I replace the innermost block with the following:
x1 := (2 / 0 "error!") > 0. Actually, it is enough to run the following stand-alone: [] ensure: [ x1 := (2 / 0 "error!") > 0 ] If you debug the Abandon button, you can see that another error occurs while terminating the process, which is a MessageNotUnderstood for #> in ZeroDivision. The only reason why a second debugger does not appear is the current bug in Process >> #terminate which "absorbs" subsequent error in this situation and which is currently being discussed in [2].
Sorry for the long message! I hope that you agree with my arguments, and if not, I am very excited to hear your ones. :-) Unless contradicted, I would like to request to change #testNestedUnwind as described above and use the changed version as the general basis for the ongoing discussions in [1, 2]. But maybe I am also just committing a fatal case of false reasoning ... :-)
Best, Christoph
[1] http://forum.world.st/The-Inbox-Kernel-ct-1405-mcz-td5129706.html [2] http://forum.world.st/stepping-over-non-local-return-in-a-protected-block-td...
Hi Christoph, I guess that the original intention was to perform the cleanup (like releasing resources) even if we close the debugger in the midst of unwinding. Whether this is a good idea or not is questionable indeed if the debugger was opened due to unhandled error during execution of unwind block. But the debugger was not necessarily opened during unwinding... The question you raise is whether we are trying to be too much clever... It might be.
Le lun. 17 mai 2021 à 17:30, Thiede, Christoph < Christoph.Thiede@student.hpi.uni-potsdam.de> a écrit :
Hi all, hi Jaromir,
I'm raising a new question in this post that is related to the following threads, but I think that it deserves its own thread due to the fundamental criticism expressed: [1, 2]
I just took a closer look at *ProcessTest >> #testNestedUnwind* and I have to say that I don't agree with it. I'm sorry that I did not mention this earlier, but somehow this aspect of Jaromir's large amount of recent work has escaped my attention before today. For reference, so that we all know what we are talking about, here is the test in question:
testNestedUnwind
"Test all nested unwind blocks are correctly unwound; all unwind blocks halfway through their execution should be completed or at least attempted to complete, not only the innermost one"
| p x1 x2 x3 |
x1 := x2 := x3 := false.
p :=
[
[
[ ] ensure: [ "halfway through completion when suspended"
[ ] ensure: [ "halfway through completion when suspended"
Processor activeProcess suspend.
x1 := true].
x2 := true]
] ensure: [ "not started yet when suspended"
x3 := true]
] fork.
Processor yield.
p terminate.
self assert: x1 & x2 & x3.
I'm not convinced about the assertions in this test. :-) In fact, I would only expect x3 to be true but x1 and x2 to be false! IMHO, when terminating a process, halfway-executed unwinded contexts should not be continued. Only not-yet-activated unwind contexts should be triggered. Here are my arguments:
- *Regular unwinding and process termination should have exactly
the same behavior.*
Assume we manipulated the example from the test like this: [ *[* [ [ ] ensure: [ "halfway through completion when suspended" [ ] ensure: [ "halfway through completion when suspended" *self error.* x1 := true]. x2 := true] ] ensure: [ "not started yet when suspended" x3 := true] *] on: Error do: []* ] fork. I have highlighted the differences, so what I changed was i) to insert an error handler at the bottom of the process and ii) instead of terminating the process, to raise an error in the innermost block. In this example, only x3 will be set to true which is because the exceptional control flow explicitly discontinues the logic running inside the error handler. Only not-yet-activated unwind contexts will be triggered as part of the unwinding, which only applies to the outermost unwind context. In my view, process termination should have exactly the same semantics as using an exception to abort the control flow. If we would not catch the error in the above example but press Abandon in the appearing debugger instead, I see no reason why we would want to execute a different set of unwind contexts.
- Last but not least, the fact that an error has been signaled means
that the signalerContext is "infected" so under no circumstances, abandoning the process should resume the execution of this infected context! (The only exception is when you consciously do so via the "Proceed" button in a debugger.) This might become more vivid if I replace the innermost block with the following:
x1 := (2 / 0 "error!") > 0.
Actually, it is enough to run the following stand-alone:
[] ensure: [
x1 := (2 / 0 "error!") > 0
]
If you debug the Abandon button, you can see that another error occurs while terminating the process, which is a *MessageNotUnderstood for #> in ZeroDivision.* The only reason why a second debugger does not appear is the current bug in Process >> #terminate which "absorbs" subsequent error in this situation and which is currently being discussed in [2].
Sorry for the long message! I hope that you agree with my arguments, and if not, I am very excited to hear your ones. :-) Unless contradicted, I would like to request to change #testNestedUnwind as described above and use the changed version as the general basis for the ongoing discussions in [1, 2]. But maybe I am also just committing a fatal case of false reasoning ... :-)
Best, Christoph
[1] http://forum.world.st/The-Inbox-Kernel-ct-1405-mcz-td5129706.html [2] http://forum.world.st/stepping-over-non-local-return-in-a-protected-block-td...
Hi Christoph,
IMHO, when terminating a process, halfway-executed unwinded contexts should not be continued. Only not-yet-activated unwind contexts should be triggered.
Yes, I too was wondering why there are different unwind semantics in various situations (error handling, active process unwind, active and suspended process termination); why there's not just one common semantics for all. My conclusion was completing half-ways through unwind blocks was way more complex than unwinding just the not-yet-started unwind blocks.
As a result (my opinion) Squeak implemented just the completion of the most recent half-ways through unwind block during termination, and VisualWorks went a step further and implemented the completion of the outer-most half-ways through unwind block.
Both however left the termination of the active process on the basic level - no completion of half-ways through blocks. In my attempt I proposed to unify the semantics of active process and suspended process termination by suspending the active process and terminating is as a suspended process.
I was considering a fun discussion about extending the error handling and unwind semantics to match the termination unwind semantics - i.e. including completion of half-ways through unwind blocks during normal returns - but that's most likely in the "way too clever" territory :)
Your proposition goes in the opposite direction however - to reduce the termination semantics to match the current error handling and active process unwind semantics.
Well, I personally prefer completing the half-ways through unwind blocks where possible. In my mind it means "try to repair or clean-up as much as possible before ending a process". I still think completing half-ways through unwind blocks is worth the extra effort.
Regarding the example:
[ [ [ [ ] ensure: [ "halfway through completion" [ ] ensure: [ "halfway through completion" self error. x1 := true]. x2 := true] ] ensure: [ "not started yet" x3 := true] ] on: Error do: [] ] fork {x1 . x2 . x3} ---> #(nil nil true)
In my view, process termination should have exactly the same semantics as using an exception to abort the control flow. If we would not catch the error in the above example but press Abandon in the appearing debugger instead, I see no reason why we would want to execute a different set of unwind contexts.
I disagree here: If we would not catch the error, we would be in a different situation: an error would have occurred which we would not have anticipated, thus abandoning the debugger would be a different intentional action than a controlled and anticipated return from an exception. I may argue we could even attempt to unwind as if terminating but I'm not sure it'd be justified. So actually I must admit a different semantics here may even be desirable. I'm not sure in this regard.
So to conclude, unification was my original driver but I'm no longer so sure... Termination may be a different beast than a regular return or handled error after all.
Thanks for this discussion, I look forward to taking a closer look at your changeset!
Hi Nicolas,
Nicolas Cellier wrote
Hi Christoph, I guess that the original intention was to perform the cleanup (like releasing resources) even if we close the debugger in the midst of unwinding. Whether this is a good idea or not is questionable indeed if the debugger was opened due to unhandled error during execution of unwind block. But the debugger was not necessarily opened during unwinding... The question you raise is whether we are trying to be too much clever... It might be.
Yes indeed, trying to be too clever is very dangerous!! I'm old enough to know first hand :D Thanks,
best,
----- ^[^ Jaromir -- Sent from: http://forum.world.st/Squeak-Dev-f45488.html
Hi Christoph,
I posted some additional arguments in http://forum.world.st/Solving-multiple-termination-bugs-summary-proposal-tp5...
[...] the fact that an error has been signaled means that the signalerContext is "infected" so under no circumstances, abandoning the process should resume the execution of this infected context!
This is what really got me thinking... yes, resuming errors sounds like a bad idea. But the point is if you terminate a totally healthy process in the middle of its unwind block then there's no reason to prevent its normal completion. The thing is you don't know in advance. But a debugger is a different story - you see an error and make a conscious decision - Proceed or Abandon? That's why I was looking for a Kill button :) Currently the consensus is Abandon = terminate, however this is not a given, it can be reconsidered... e.g. use the unwind version of regular #return/#resume/ etc - without unwinding halfway through block - that could make a good sense...
It means two different unwind semantics could really be desirable and justified: If a healthy process terminates, let it unwind as much as possible including all unwind blocks halfway-through execution.
If a broken process terminates via Abandoning the debugger, use the current "return" unwind semantics - i.e. execute only not-yet-started unwind blocks.
What do you think?
I'm looking forward to your thoughts. best,
----- ^[^ Jaromir -- Sent from: http://forum.world.st/Squeak-Dev-f45488.html
Hi Jaromir, hi Nicolas,
thanks for your feedback. I think I see the conflict between useful clean-ups during termination on the one hand and way-too-clever clean-ups during abandoning an errored process on the other hand.
Jaromir, your proposal to provide multiple selectors for modeling separate modes of termination sounds like a very good idea to me. But how many different modes do we actually need? So far I can count three modes:
(i) run no unwind contexts (harhest possible way; currently only achievable by doing "suspendedContext privSender: nil" prior to terminating) (ii) run not-yet started unwind contexts (this is what I proposed in fix-Process-terminate.1.cs [1]) (iii) run all unwind contexts, including those that already have been started (this is the most friendly way that you implemented in #terminate recently)
Can you please confirm whether this enumeration is correct and complete?
What seems disputable to me are the following questions:
1. Which mode should we use in which situations?
I think this debate could benefit from a few more concrete usage scenarios. I'm just collecting some here (thinking aloud):
- Process Browser: We can provide multiple options in the process menu. - Debugger: I agree with you that Abandon should always run not-yet started unwind contexts but never resume halfway-executed unwind contexts. So this maps to to mode (ii) from above. - Skimming through most senders of #terminate in the image, they often orchestrate helper processes, deal with unhandled errors or timeouts, or do similar stuff - usually they should be very fine with the friendly version of #terminate, i.e. mode (iii) from above. I think. - Regarding option (1), I think you would need it extremely seldom but maybe in situations like when your stack contains a loop, your unwind contexts will cause a recursion/new error, or you deliberately want to prevent any unwind context from running. No objections against adding a small but decent button for this in the debugger. :-)
Would you agree with these behaviors? Maybe you can add further examples to the list?
2. How should we name them?
Direct proposal: (i) #kill and (iii) #terminate. After looking up the original behavior of #terminate in Squeak 5.3, I think it would be consistent to resume all halfway-executed unwind contexts in this method. So yes, I also withdraw my criticism about #testNestedUnwind. :-)
But I don't have any good idea for version (ii) yet. Call it #abandon like in the debugger? Then again, #abandon is rather a verb from the Morphic language. Further possible vocables (according to my synonym thesaurus) include #end, #stop, #finish, #unwind, #abort, #exit. Please help... :-)
Best, Christoph
[1] http://forum.world.st/template/NamlServlet.jtp?macro=print_post&node=512...
Hi Christoph,
Jaromir, your proposal to provide multiple selectors for modeling separate modes of termination sounds like a very good idea to me. But how many different modes do we actually need? So far I can count three modes:
(i) run no unwind contexts (harshest possible way; currently only achievable by doing "suspendedContext privSender: nil" prior to terminating) (ii) run not-yet started unwind contexts (this is what I proposed in fix-Process-terminate.1.cs [1]) (iii) run all unwind contexts, including those that already have been started (this is the most friendly way that you implemented in #terminate recently)
I think this is it.
Litereally minutes ago had to use privSender: nil to get rid of a debugger :) Fully terminate really is too strong to recover from fatal errors.
... my point here is: Proceeding from an error almost always doesn't seem "right". :-) It is always a decision by the debugging programmer to override the default control flow and switch to the "next plausible alternative control flow", i.e., resume as if the error would have never been raised.
yes - I'd add: even an error may quite often be completely benign, like 'Transcript show: 1/0' - possibly a typo so you just may want to Proceed or fully terminate. In case the error damages a whole subsequent chain of events, you're absolutely right a full termination seems a silly option and a light version of terminate may be the most appropriate.
So I fully agree the decision which termination mode it is stays with the user - so I'm all for giving the user the choices you suggested.
\1. Which mode should we use in which situations?
I think this debate could benefit from a few more concrete usage scenarios. I'm just collecting some here (thinking aloud):
- Process Browser: We can provide multiple options in the process menu. - Debugger: I agree with you that Abandon should always run not-yet started unwind contexts but never resume halfway-executed unwind contexts. So this maps to to mode (ii) from above. - Skimming through most senders of #terminate in the image, they often orchestrate helper processes, deal with unhandled errors or timeouts, or do similar stuff - usually they should be very fine with the friendly version of #terminate, i.e. mode (iii) from above. I think. - Regarding option (1), I think you would need it extremely seldom but maybe in situations like when your stack contains a loop, your unwind contexts will cause a recursion/new error, or you deliberately want to prevent any unwind context from running. No objections against adding a small but decent button for this in the debugger. :-)
Would you agree with these behaviors? Maybe you can add further examples to the list?
Yes
Process Browser - the right click menu could provide all options
Debugger - Abandon could be the lightweight version you proposed. Why not have a proper Abandon button for it? The right click menu on a context could offer the Kill option (next to 'peel to first like this'); no button necessary. Now the question is what should be under the "window close" red-circle-x - heavyweight terminate? I'm thinking this scenario: if the debugger returns after closing the window you start thinking what happened and use Abandon; if it still doesn't help you go right-click and kill it?
My usual scenario is (limited experience however): look at something in the debugger (on a healthy process) and close the window (i.e. full termination is appropriate and I'd even say preferable). If something goes wrong - then I'd welcome a hint there are options - thus the proper Abandon button - what do you think?
\2. How should we name them?
Direct proposal: (i) #kill and (iii) #terminate. After looking up the original behavior of #terminate in Squeak 5.3, I think it would be consistent to resume all halfway-executed unwind contexts in this method. So yes, I also withdraw my criticism about #testNestedUnwind. :-)
But I don't have any good idea for version (ii) yet. Call it #abandon like in the debugger? Then again, #abandon is rather a verb from the Morphic language. Further possible vocables (according to my synonym thesaurus) include #end, #stop, #finish, #unwind, #abort, #exit. Please help... :-)
I'd probably go with something like #terminateLight because it's a proper process termination including unwinds except the ones currently in progress - so it is a light version of #terminate :) I've checked VisualWorks: they chose #terminateUnsafely for this type of termination which I don't like much, it sounds too negative; the real meaning is rather #terminateAsSafelyAsPossibleGivenTheCircumstances ;).
I'm wondering whether #unwindTo: (used ony by Generator) is bugged (with regard to dealing with non-local returns), and could be fixed/unified with your approach. Look at these examples: ``` p := [[Processor activeProcess suspend] valueUninterruptably] fork. Processor yield. p suspendedContext unwindTo: nil ``` or ``` p := [[:exit | [Processor activeProcess suspend] ensure: [exit value]] valueWithExit] fork. Processor yield. p suspendedContext unwindTo: nil ```
If you do `p terminate` instead of `p suspendedContext unwindTo: nil`, it works fine, but #unwindTo causes a block cannot return error - I think it's the same bug all over again :) #value evaluates the non-local return on the wrong stack...
Regarding our cannot return discussion - I have to think about it and I'll post my reply later in [1] to keep it separate :)
Thanks again and regards,
[1] http://forum.world.st/The-Inbox-Kernel-ct-1405-mcz-td5129706.html#a5130114
----- ^[^ Jaromir -- Sent from: http://forum.world.st/Squeak-Dev-f45488.html
Hi Jaromir,
back from vacation, *finally* I have found some time to return back to this pleasant problem! :-)
Great, it sounds like we're already agreeing pretty much, just let me address some small points:
Debugger - Abandon could be the lightweight version you proposed. Why not have a proper Abandon button for it? The right click menu on a context could offer the Kill option (next to 'peel to first like this'); no button necessary. Now the question is what should be under the "window close" red-circle-x - heavyweight terminate? I'm thinking this scenario: if the debugger returns after closing the window you start thinking what happened and use Abandon; if it still doesn't help you go right-click and kill it?
My usual scenario is (limited experience however): look at something in the debugger (on a healthy process) and close the window (i.e. full termination is appropriate and I'd even say preferable). If something goes wrong - then I'd welcome a hint there are options - thus the proper Abandon button - what do you think?
Hm, I would rather say that closing the window should have the same effect as pressing "abandon" - personally, I am very much used to this and we do not really have good options to communicate a different behavior of the close button. I think presenting the new behaviors Kill (i) and (iii) in some kind of menu would be less confusing. A kill item in the stack trace menu sounds fine for now (even though killing is not actually related to the selected context). In the long term, I would like to introduce menu buttons in the fashion of [1] in the Morphic UI (Marcel is currently reviewing my proposal for this), then we could move Kill into the Abandon menu, but for now, your proposal sounds perfectly fine to me. :-)
look at something in the debugger (on a healthy process) and close the window (i.e. full termination is appropriate and I'd even say preferable).
Can you help me please, if the process is healthy (so no unwind context is lying on the stack), would there be any difference between the different terminate modi (ii) and (iii)?
But I don't have any good idea for version (ii) yet. Call it #abandon like in the debugger? Then again, #abandon is rather a verb from the Morphic language. Further possible vocables (according to my synonym thesaurus) include #end, #stop, #finish, #unwind, #abort, #exit. Please help... :-)
I'd probably go with something like #terminateLight because it's a proper process termination including unwinds except the ones currently in progress
- so it is a light version of #terminate :) I've checked VisualWorks: they
chose #terminateUnsafely for this type of termination which I don't like much, it sounds too negative; the real meaning is rather #terminateAsSafelyAsPossibleGivenTheCircumstances ;).
Hm, "light" reminds me of "friendly" and (iii) would certainly be more friendly (giving the process more freedom for cleaning things up). Difficult ... What about #terminateNow for (ii) and #terminate for (iii), slightly related to #updateStyle and #updateStyleNow? Since (iii) might continue with the execution of the current halfway-executed context, (ii) is more urgent. Another option would be "terminate: aggressive", analogously to "cleanup: aggressive" on Behavior where #terminate would fall back to "terminate: false". But this would make the new mode a little bit worse accessible.
Concerning your #unwindTo: issue - not sure if this actually related to this thread? Actually I've lost the overview which terminate mode - (ii) or (iii) - you are referring to, maybe we should discuss this in a separate conversation. :-)
---
TLDR: Concerning our next steps - consider this a small to-do list:
- [ ] We want to implement three modi of process termination: - [ ] (i) is not yet implemented but should be easy to implement - [ ] (ii) is in my proposal from [2], maybe needs further refinement - [ ] (iii) is pretty much what we have in Process >> #terminate in the current Trunk. Is there anything still missing yet? - [ ] Decide on names for (ii) and (iii) - see above - [ ] Decide on UI integration into debugger - see above
Further answers following soon.
Best, Christoph
[1] https://user-images.githubusercontent.com/48567372/60924070-4472fe80-a26e-11... [2] http://lists.squeakfoundation.org/pipermail/squeak-dev/2021-May/215608.html (fix-Process-terminate.cs)
--- Sent from Squeak Inbox Talk
On 2021-05-29T14:31:13-05:00, m@jaromir.net wrote:
Hi Christoph,
Jaromir, your proposal to provide multiple selectors for modeling separate modes of termination sounds like a very good idea to me. But how many different modes do we actually need? So far I can count three modes:
(i) run no unwind contexts (harshest possible way; currently only achievable by doing "suspendedContext privSender: nil" prior to terminating) (ii) run not-yet started unwind contexts (this is what I proposed in fix-Process-terminate.1.cs [1]) (iii) run all unwind contexts, including those that already have been started (this is the most friendly way that you implemented in #terminate recently)
I think this is it.
Litereally minutes ago had to use privSender: nil to get rid of a debugger :) Fully terminate really is too strong to recover from fatal errors.
... my point here is: Proceeding from an error almost always doesn't seem "right". :-) It is always a decision by the debugging programmer to override the default control flow and switch to the "next plausible alternative control flow", i.e., resume as if the error would have never been raised.
yes - I'd add: even an error may quite often be completely benign, like 'Transcript show: 1/0' - possibly a typo so you just may want to Proceed or fully terminate. In case the error damages a whole subsequent chain of events, you're absolutely right a full termination seems a silly option and a light version of terminate may be the most appropriate.
So I fully agree the decision which termination mode it is stays with the user - so I'm all for giving the user the choices you suggested.
\1. Which mode should we use in which situations?
I think this debate could benefit from a few more concrete usage scenarios. I'm just collecting some here (thinking aloud):
- Process Browser: We can provide multiple options in the process menu. - Debugger: I agree with you that Abandon should always run not-yet started unwind contexts but never resume halfway-executed unwind contexts. So this maps to to mode (ii) from above. - Skimming through most senders of #terminate in the image, they often orchestrate helper processes, deal with unhandled errors or timeouts, or do similar stuff - usually they should be very fine with the friendly version of #terminate, i.e. mode (iii) from above. I think. - Regarding option (1), I think you would need it extremely seldom but maybe in situations like when your stack contains a loop, your unwind contexts will cause a recursion/new error, or you deliberately want to prevent any unwind context from running. No objections against adding a small but decent button for this in the debugger. :-)
Would you agree with these behaviors? Maybe you can add further examples to the list?
Yes
Process Browser - the right click menu could provide all options
Debugger - Abandon could be the lightweight version you proposed. Why not have a proper Abandon button for it? The right click menu on a context could offer the Kill option (next to 'peel to first like this'); no button necessary. Now the question is what should be under the "window close" red-circle-x - heavyweight terminate? I'm thinking this scenario: if the debugger returns after closing the window you start thinking what happened and use Abandon; if it still doesn't help you go right-click and kill it?
My usual scenario is (limited experience however): look at something in the debugger (on a healthy process) and close the window (i.e. full termination is appropriate and I'd even say preferable). If something goes wrong - then I'd welcome a hint there are options - thus the proper Abandon button - what do you think?
\2. How should we name them?
Direct proposal: (i) #kill and (iii) #terminate. After looking up the original behavior of #terminate in Squeak 5.3, I think it would be consistent to resume all halfway-executed unwind contexts in this method. So yes, I also withdraw my criticism about #testNestedUnwind. :-)
But I don't have any good idea for version (ii) yet. Call it #abandon like in the debugger? Then again, #abandon is rather a verb from the Morphic language. Further possible vocables (according to my synonym thesaurus) include #end, #stop, #finish, #unwind, #abort, #exit. Please help... :-)
I'd probably go with something like #terminateLight because it's a proper process termination including unwinds except the ones currently in progress
- so it is a light version of #terminate :) I've checked VisualWorks: they
chose #terminateUnsafely for this type of termination which I don't like much, it sounds too negative; the real meaning is rather #terminateAsSafelyAsPossibleGivenTheCircumstances ;).
I'm wondering whether #unwindTo: (used ony by Generator) is bugged (with regard to dealing with non-local returns), and could be fixed/unified with your approach. Look at these examples:
p := [[Processor activeProcess suspend] valueUninterruptably] fork. Processor yield. p suspendedContext unwindTo: nil
or
p := [[:exit | [Processor activeProcess suspend] ensure: [exit value]] valueWithExit] fork. Processor yield. p suspendedContext unwindTo: nil
If you do `p terminate` instead of `p suspendedContext unwindTo: nil`, it works fine, but #unwindTo causes a block cannot return error - I think it's the same bug all over again :) #value evaluates the non-local return on the wrong stack...
Regarding our cannot return discussion - I have to think about it and I'll post my reply later in [1] to keep it separate :)
Thanks again and regards,
[1] http://forum.world.st/The-Inbox-Kernel-ct-1405-mcz-td5129706.html#a5130114
^[^ Jaromir
Sent from: http://forum.world.st/Squeak-Dev-f45488.html
Hi all, hi Jaromir,
based on the to-do list from my previous message, I have created a changeset that adds Process >> #terminateAggressively (ii) and Process >> #kill (i) and integrates them into the process browser and the debugger. Most importantly, the following snippet will inform "nil" now instead of "ZeroDevision" after abandoning the error:
[[x1 := nil] ensure: [x1 := (2 / 0)]] ensure: [self inform: x1 asString].
With this changeset, I would be fully happy again with the functionality of the debugger. :-) What remains to do is the following:
- [ ] We want to implement three modi of process termination: - [x] (i) - see #kill - [X] (ii) - see #terminateAggressively - [ ] (iii) is pretty much what we have in Process >> #terminate in the current Trunk. Is there anything still missing yet? - [ ] Decide on names for (ii) and (iii) - see my previous message - [ ] Decide on UI integration into debugger - proposal exists in Process-terminateAggressively.1, but open for criticism - [ ] Test #terminateAggressively (actualy, I don't have a good of overview of all your new #terminate tests - probably it would be easiest to wait until they have arrived in the Trunk and then create copies of them to test #terminateAggressively analogously :D)
Best, Christoph
--- Sent from Squeak Inbox Talk
On 2021-08-22T16:07:54+02:00, christoph.thiede@student.hpi.uni-potsdam.de wrote:
Hi Jaromir,
back from vacation, *finally* I have found some time to return back to this pleasant problem! :-)
Great, it sounds like we're already agreeing pretty much, just let me address some small points:
Debugger - Abandon could be the lightweight version you proposed. Why not have a proper Abandon button for it? The right click menu on a context could offer the Kill option (next to 'peel to first like this'); no button necessary. Now the question is what should be under the "window close" red-circle-x - heavyweight terminate? I'm thinking this scenario: if the debugger returns after closing the window you start thinking what happened and use Abandon; if it still doesn't help you go right-click and kill it?
My usual scenario is (limited experience however): look at something in the debugger (on a healthy process) and close the window (i.e. full termination is appropriate and I'd even say preferable). If something goes wrong - then I'd welcome a hint there are options - thus the proper Abandon button - what do you think?
Hm, I would rather say that closing the window should have the same effect as pressing "abandon" - personally, I am very much used to this and we do not really have good options to communicate a different behavior of the close button. I think presenting the new behaviors Kill (i) and (iii) in some kind of menu would be less confusing. A kill item in the stack trace menu sounds fine for now (even though killing is not actually related to the selected context). In the long term, I would like to introduce menu buttons in the fashion of [1] in the Morphic UI (Marcel is currently reviewing my proposal for this), then we could move Kill into the Abandon menu, but for now, your proposal sounds perfectly fine to me. :-)
look at something in the debugger (on a healthy process) and close the window (i.e. full termination is appropriate and I'd even say preferable).
Can you help me please, if the process is healthy (so no unwind context is lying on the stack), would there be any difference between the different terminate modi (ii) and (iii)?
But I don't have any good idea for version (ii) yet. Call it #abandon like in the debugger? Then again, #abandon is rather a verb from the Morphic language. Further possible vocables (according to my synonym thesaurus) include #end, #stop, #finish, #unwind, #abort, #exit. Please help... :-)
I'd probably go with something like #terminateLight because it's a proper process termination including unwinds except the ones currently in progress
- so it is a light version of #terminate :) I've checked VisualWorks: they
chose #terminateUnsafely for this type of termination which I don't like much, it sounds too negative; the real meaning is rather #terminateAsSafelyAsPossibleGivenTheCircumstances ;).
Hm, "light" reminds me of "friendly" and (iii) would certainly be more friendly (giving the process more freedom for cleaning things up). Difficult ... What about #terminateNow for (ii) and #terminate for (iii), slightly related to #updateStyle and #updateStyleNow? Since (iii) might continue with the execution of the current halfway-executed context, (ii) is more urgent. Another option would be "terminate: aggressive", analogously to "cleanup: aggressive" on Behavior where #terminate would fall back to "terminate: false". But this would make the new mode a little bit worse accessible.
Concerning your #unwindTo: issue - not sure if this actually related to this thread? Actually I've lost the overview which terminate mode - (ii) or (iii) - you are referring to, maybe we should discuss this in a separate conversation. :-)
TLDR: Concerning our next steps - consider this a small to-do list:
- [ ] We want to implement three modi of process termination:
- [ ] (i) is not yet implemented but should be easy to implement - [ ] (ii) is in my proposal from [2], maybe needs further refinement - [ ] (iii) is pretty much what we have in Process >> #terminate in the current Trunk. Is there anything still missing yet?
- [ ] Decide on names for (ii) and (iii) - see above
- [ ] Decide on UI integration into debugger - see above
Further answers following soon.
Best, Christoph
[1] https://user-images.githubusercontent.com/48567372/60924070-4472fe80-a26e-11... [2] http://lists.squeakfoundation.org/pipermail/squeak-dev/2021-May/215608.html (fix-Process-terminate.cs)
Sent from Squeak Inbox Talk
On 2021-05-29T14:31:13-05:00, m at jaromir.net wrote:
Hi Christoph,
Jaromir, your proposal to provide multiple selectors for modeling separate modes of termination sounds like a very good idea to me. But how many different modes do we actually need? So far I can count three modes:
(i) run no unwind contexts (harshest possible way; currently only achievable by doing "suspendedContext privSender: nil" prior to terminating) (ii) run not-yet started unwind contexts (this is what I proposed in fix-Process-terminate.1.cs [1]) (iii) run all unwind contexts, including those that already have been started (this is the most friendly way that you implemented in #terminate recently)
I think this is it.
Litereally minutes ago had to use privSender: nil to get rid of a debugger :) Fully terminate really is too strong to recover from fatal errors.
... my point here is: Proceeding from an error almost always doesn't seem "right". :-) It is always a decision by the debugging programmer to override the default control flow and switch to the "next plausible alternative control flow", i.e., resume as if the error would have never been raised.
yes - I'd add: even an error may quite often be completely benign, like 'Transcript show: 1/0' - possibly a typo so you just may want to Proceed or fully terminate. In case the error damages a whole subsequent chain of events, you're absolutely right a full termination seems a silly option and a light version of terminate may be the most appropriate.
So I fully agree the decision which termination mode it is stays with the user - so I'm all for giving the user the choices you suggested.
\1. Which mode should we use in which situations?
I think this debate could benefit from a few more concrete usage scenarios. I'm just collecting some here (thinking aloud):
- Process Browser: We can provide multiple options in the process menu. - Debugger: I agree with you that Abandon should always run not-yet started unwind contexts but never resume halfway-executed unwind contexts. So this maps to to mode (ii) from above. - Skimming through most senders of #terminate in the image, they often orchestrate helper processes, deal with unhandled errors or timeouts, or do similar stuff - usually they should be very fine with the friendly version of #terminate, i.e. mode (iii) from above. I think. - Regarding option (1), I think you would need it extremely seldom but maybe in situations like when your stack contains a loop, your unwind contexts will cause a recursion/new error, or you deliberately want to prevent any unwind context from running. No objections against adding a small but decent button for this in the debugger. :-)
Would you agree with these behaviors? Maybe you can add further examples to the list?
Yes
Process Browser - the right click menu could provide all options
Debugger - Abandon could be the lightweight version you proposed. Why not have a proper Abandon button for it? The right click menu on a context could offer the Kill option (next to 'peel to first like this'); no button necessary. Now the question is what should be under the "window close" red-circle-x - heavyweight terminate? I'm thinking this scenario: if the debugger returns after closing the window you start thinking what happened and use Abandon; if it still doesn't help you go right-click and kill it?
My usual scenario is (limited experience however): look at something in the debugger (on a healthy process) and close the window (i.e. full termination is appropriate and I'd even say preferable). If something goes wrong - then I'd welcome a hint there are options - thus the proper Abandon button - what do you think?
\2. How should we name them?
Direct proposal: (i) #kill and (iii) #terminate. After looking up the original behavior of #terminate in Squeak 5.3, I think it would be consistent to resume all halfway-executed unwind contexts in this method. So yes, I also withdraw my criticism about #testNestedUnwind. :-)
But I don't have any good idea for version (ii) yet. Call it #abandon like in the debugger? Then again, #abandon is rather a verb from the Morphic language. Further possible vocables (according to my synonym thesaurus) include #end, #stop, #finish, #unwind, #abort, #exit. Please help... :-)
I'd probably go with something like #terminateLight because it's a proper process termination including unwinds except the ones currently in progress
- so it is a light version of #terminate :) I've checked VisualWorks: they
chose #terminateUnsafely for this type of termination which I don't like much, it sounds too negative; the real meaning is rather #terminateAsSafelyAsPossibleGivenTheCircumstances ;).
I'm wondering whether #unwindTo: (used ony by Generator) is bugged (with regard to dealing with non-local returns), and could be fixed/unified with your approach. Look at these examples:
p := [[Processor activeProcess suspend] valueUninterruptably] fork. Processor yield. p suspendedContext unwindTo: nil
or
p := [[:exit | [Processor activeProcess suspend] ensure: [exit value]] valueWithExit] fork. Processor yield. p suspendedContext unwindTo: nil
If you do `p terminate` instead of `p suspendedContext unwindTo: nil`, it works fine, but #unwindTo causes a block cannot return error - I think it's the same bug all over again :) #value evaluates the non-local return on the wrong stack...
Regarding our cannot return discussion - I have to think about it and I'll post my reply later in [1] to keep it separate :)
Thanks again and regards,
[1] http://forum.world.st/The-Inbox-Kernel-ct-1405-mcz-td5129706.html#a5130114
^[^ Jaromir
Sent from: http://forum.world.st/Squeak-Dev-f45488.html
Hi Christoph --
How would you summarize the differences between #terminate, #terminateAggressively, and #kill, each with 1-2 sentences? :-)
Best, Marcel Am 23.08.2021 13:18:40 schrieb christoph.thiede@student.hpi.uni-potsdam.de christoph.thiede@student.hpi.uni-potsdam.de: Hi all, hi Jaromir,
based on the to-do list from my previous message, I have created a changeset that adds Process >> #terminateAggressively (ii) and Process >> #kill (i) and integrates them into the process browser and the debugger. Most importantly, the following snippet will inform "nil" now instead of "ZeroDevision" after abandoning the error:
[[x1 := nil] ensure: [x1 := (2 / 0)]] ensure: [self inform: x1 asString].
With this changeset, I would be fully happy again with the functionality of the debugger. :-) What remains to do is the following:
- [ ] We want to implement three modi of process termination: - [x] (i) - see #kill - [X] (ii) - see #terminateAggressively - [ ] (iii) is pretty much what we have in Process >> #terminate in the current Trunk. Is there anything still missing yet? - [ ] Decide on names for (ii) and (iii) - see my previous message - [ ] Decide on UI integration into debugger - proposal exists in Process-terminateAggressively.1, but open for criticism - [ ] Test #terminateAggressively (actualy, I don't have a good of overview of all your new #terminate tests - probably it would be easiest to wait until they have arrived in the Trunk and then create copies of them to test #terminateAggressively analogously :D)
Best, Christoph
--- Sent from Squeak Inbox Talk [https://github.com/hpi-swa-lab/squeak-inbox-talk]
On 2021-08-22T16:07:54+02:00, christoph.thiede@student.hpi.uni-potsdam.de wrote:
Hi Jaromir,
back from vacation, *finally* I have found some time to return back to this pleasant problem! :-)
Great, it sounds like we're already agreeing pretty much, just let me address some small points:
Debugger - Abandon could be the lightweight version you proposed. Why not have a proper Abandon button for it? The right click menu on a context could offer the Kill option (next to 'peel to first like this'); no button necessary. Now the question is what should be under the "window close" red-circle-x - heavyweight terminate? I'm thinking this scenario: if the debugger returns after closing the window you start thinking what happened and use Abandon; if it still doesn't help you go right-click and kill it?
My usual scenario is (limited experience however): look at something in the debugger (on a healthy process) and close the window (i.e. full termination is appropriate and I'd even say preferable). If something goes wrong - then I'd welcome a hint there are options - thus the proper Abandon button - what do you think?
Hm, I would rather say that closing the window should have the same effect as pressing "abandon" - personally, I am very much used to this and we do not really have good options to communicate a different behavior of the close button. I think presenting the new behaviors Kill (i) and (iii) in some kind of menu would be less confusing. A kill item in the stack trace menu sounds fine for now (even though killing is not actually related to the selected context). In the long term, I would like to introduce menu buttons in the fashion of [1] in the Morphic UI (Marcel is currently reviewing my proposal for this), then we could move Kill into the Abandon menu, but for now, your proposal sounds perfectly fine to me. :-)
look at something in the debugger (on a healthy process) and close the window (i.e. full termination is appropriate and I'd even say preferable).
Can you help me please, if the process is healthy (so no unwind context is lying on the stack), would there be any difference between the different terminate modi (ii) and (iii)?
But I don't have any good idea for version (ii) yet. Call it #abandon like in the debugger? Then again, #abandon is rather a verb from the Morphic language. Further possible vocables (according to my synonym thesaurus) include #end, #stop, #finish, #unwind, #abort, #exit. Please help... :-)
I'd probably go with something like #terminateLight because it's a proper process termination including unwinds except the ones currently in progress
- so it is a light version of #terminate :) I've checked VisualWorks: they
chose #terminateUnsafely for this type of termination which I don't like much, it sounds too negative; the real meaning is rather #terminateAsSafelyAsPossibleGivenTheCircumstances ;).
Hm, "light" reminds me of "friendly" and (iii) would certainly be more friendly (giving the process more freedom for cleaning things up). Difficult ... What about #terminateNow for (ii) and #terminate for (iii), slightly related to #updateStyle and #updateStyleNow? Since (iii) might continue with the execution of the current halfway-executed context, (ii) is more urgent. Another option would be "terminate: aggressive", analogously to "cleanup: aggressive" on Behavior where #terminate would fall back to "terminate: false". But this would make the new mode a little bit worse accessible.
Concerning your #unwindTo: issue - not sure if this actually related to this thread? Actually I've lost the overview which terminate mode - (ii) or (iii) - you are referring to, maybe we should discuss this in a separate conversation. :-)
TLDR: Concerning our next steps - consider this a small to-do list:
- [ ] We want to implement three modi of process termination:
- [ ] (i) is not yet implemented but should be easy to implement - [ ] (ii) is in my proposal from [2], maybe needs further refinement - [ ] (iii) is pretty much what we have in Process >> #terminate in the current Trunk. Is there anything still missing yet?
- [ ] Decide on names for (ii) and (iii) - see above
- [ ] Decide on UI integration into debugger - see above
Further answers following soon.
Best, Christoph
[1] https://user-images.githubusercontent.com/48567372/60924070-4472fe80-a26e-11... [2] http://lists.squeakfoundation.org/pipermail/squeak-dev/2021-May/215608.html (fix-Process-terminate.cs)
Sent from Squeak Inbox Talk
On 2021-05-29T14:31:13-05:00, m at jaromir.net wrote:
Hi Christoph,
Jaromir, your proposal to provide multiple selectors for modeling separate modes of termination sounds like a very good idea to me. But how many different modes do we actually need? So far I can count three modes:
(i) run no unwind contexts (harshest possible way; currently only achievable by doing "suspendedContext privSender: nil" prior to terminating) (ii) run not-yet started unwind contexts (this is what I proposed in fix-Process-terminate.1.cs [1]) (iii) run all unwind contexts, including those that already have been started (this is the most friendly way that you implemented in #terminate recently)
I think this is it.
Litereally minutes ago had to use privSender: nil to get rid of a debugger :) Fully terminate really is too strong to recover from fatal errors.
... my point here is: Proceeding from an error almost always doesn't seem "right". :-) It is always a decision by the debugging programmer to override the default control flow and switch to the "next plausible alternative control flow", i.e., resume as if the error would have never been raised.
yes - I'd add: even an error may quite often be completely benign, like 'Transcript show: 1/0' - possibly a typo so you just may want to Proceed or fully terminate. In case the error damages a whole subsequent chain of events, you're absolutely right a full termination seems a silly option and a light version of terminate may be the most appropriate.
So I fully agree the decision which termination mode it is stays with the user - so I'm all for giving the user the choices you suggested.
\1. Which mode should we use in which situations?
I think this debate could benefit from a few more concrete usage scenarios. I'm just collecting some here (thinking aloud):
- Process Browser: We can provide multiple options in the process menu. - Debugger: I agree with you that Abandon should always run not-yet started unwind contexts but never resume halfway-executed unwind contexts. So this maps to to mode (ii) from above. - Skimming through most senders of #terminate in the image, they often orchestrate helper processes, deal with unhandled errors or timeouts, or do similar stuff - usually they should be very fine with the friendly version of #terminate, i.e. mode (iii) from above. I think. - Regarding option (1), I think you would need it extremely seldom but maybe in situations like when your stack contains a loop, your unwind contexts will cause a recursion/new error, or you deliberately want to prevent any unwind context from running. No objections against adding a small but decent button for this in the debugger. :-)
Would you agree with these behaviors? Maybe you can add further examples to the list?
Yes
Process Browser - the right click menu could provide all options
Debugger - Abandon could be the lightweight version you proposed. Why not have a proper Abandon button for it? The right click menu on a context could offer the Kill option (next to 'peel to first like this'); no button necessary. Now the question is what should be under the "window close" red-circle-x - heavyweight terminate? I'm thinking this scenario: if the debugger returns after closing the window you start thinking what happened and use Abandon; if it still doesn't help you go right-click and kill it?
My usual scenario is (limited experience however): look at something in the debugger (on a healthy process) and close the window (i.e. full termination is appropriate and I'd even say preferable). If something goes wrong - then I'd welcome a hint there are options - thus the proper Abandon button - what do you think?
\2. How should we name them?
Direct proposal: (i) #kill and (iii) #terminate. After looking up the original behavior of #terminate in Squeak 5.3, I think it would be consistent to resume all halfway-executed unwind contexts in this method. So yes, I also withdraw my criticism about #testNestedUnwind. :-)
But I don't have any good idea for version (ii) yet. Call it #abandon like in the debugger? Then again, #abandon is rather a verb from the Morphic language. Further possible vocables (according to my synonym thesaurus) include #end, #stop, #finish, #unwind, #abort, #exit. Please help... :-)
I'd probably go with something like #terminateLight because it's a proper process termination including unwinds except the ones currently in progress
- so it is a light version of #terminate :) I've checked VisualWorks: they
chose #terminateUnsafely for this type of termination which I don't like much, it sounds too negative; the real meaning is rather #terminateAsSafelyAsPossibleGivenTheCircumstances ;).
I'm wondering whether #unwindTo: (used ony by Generator) is bugged (with regard to dealing with non-local returns), and could be fixed/unified with your approach. Look at these examples:
p := [[Processor activeProcess suspend] valueUninterruptably] fork. Processor yield. p suspendedContext unwindTo: nil
or
p := [[:exit | [Processor activeProcess suspend] ensure: [exit value]] valueWithExit] fork. Processor yield. p suspendedContext unwindTo: nil
If you do `p terminate` instead of `p suspendedContext unwindTo: nil`, it works fine, but #unwindTo causes a block cannot return error - I think it's the same bug all over again :) #value evaluates the non-local return on the wrong stack...
Regarding our cannot return discussion - I have to think about it and I'll post my reply later in [1] to keep it separate :)
Thanks again and regards,
[1] http://forum.world.st/The-Inbox-Kernel-ct-1405-mcz-td5129706.html#a5130114
^[^ Jaromir
Sent from: http://forum.world.st/Squeak-Dev-f45488.html
Hi Marcel, hi all,
How would you summarize the differences between #terminate, #terminateAggressively, and #kill, each with 1-2 sentences? :-)
Let's keep using the numbers from above instead, as the names are only my imperfect proposals:
(i) run no unwind contexts (harshest possible way; currently only achievable by doing "suspendedContext privSender: nil" prior to terminating). You would need this very rarely, if at all, for troubleshooting. (ii) run not-yet started unwind contexts (this is what I proposed in fix-Process-terminate.1.cs [1]). This is the behavior we expect from the Abandon button in a debugger. I think? (iii) run all unwind contexts, including those that already have been started (this is the most friendly way that you implemented in #terminate recently). This is the behavior we expect when terminating a healthy process.
Here are some ideas for better names:
(i) #destroy (as opposed to #kill, this sounds more like the cheap and unreactable operation it is) (ii) #terminateAggressively, which redirects to #terminate: true (iii) #terminate, which redirects to #terminate: false
Please find the attached changeset in which I have changed the names accordingly and also have removed the kill/destroy operation from the UI as per request of Marcel. I aggree with his argument that destroy is a kind of private operation that affects the interna of the process and should be only used for troubleshooting, so we should not give this hack too much visibility.
Best, Christoph
--- Sent from Squeak Inbox Talk
On 2021-08-23T13:59:04+02:00, marcel.taeumel@hpi.de wrote:
Hi Christoph --
How would you summarize the differences between #terminate, #terminateAggressively, and #kill, each with 1-2 sentences? :-)
Best, Marcel Am 23.08.2021 13:18:40 schrieb christoph.thiede at student.hpi.uni-potsdam.de <christoph.thiede at student.hpi.uni-potsdam.de>: Hi all, hi Jaromir,
based on the to-do list from my previous message, I have created a changeset that adds Process >> #terminateAggressively (ii) and Process >> #kill (i) and integrates them into the process browser and the debugger. Most importantly, the following snippet will inform "nil" now instead of "ZeroDevision" after abandoning the error:
[[x1 := nil] ensure: [x1 := (2 / 0)]] ensure: [self inform: x1 asString].
With this changeset, I would be fully happy again with the functionality of the debugger. :-) What remains to do is the following:
- [ ] We want to implement three modi of process termination:
- [x] (i) - see #kill
- [X] (ii) - see #terminateAggressively
- [ ] (iii) is pretty much what we have in Process >> #terminate in the current Trunk. Is there anything still missing yet?
- [ ] Decide on names for (ii) and (iii) - see my previous message
- [ ] Decide on UI integration into debugger - proposal exists in Process-terminateAggressively.1, but open for criticism
- [ ] Test #terminateAggressively (actualy, I don't have a good of overview of all your new #terminate tests - probably it would be easiest to wait until they have arrived in the Trunk and then create copies of them to test #terminateAggressively analogously :D)
Best, Christoph
Sent from Squeak Inbox Talk [https://github.com/hpi-swa-lab/squeak-inbox-talk]
On 2021-08-22T16:07:54+02:00, christoph.thiede at student.hpi.uni-potsdam.de wrote:
Hi Jaromir,
back from vacation, *finally* I have found some time to return back to this pleasant problem! :-)
Great, it sounds like we're already agreeing pretty much, just let me address some small points:
Debugger - Abandon could be the lightweight version you proposed. Why not have a proper Abandon button for it? The right click menu on a context could offer the Kill option (next to 'peel to first like this'); no button necessary. Now the question is what should be under the "window close" red-circle-x - heavyweight terminate? I'm thinking this scenario: if the debugger returns after closing the window you start thinking what happened and use Abandon; if it still doesn't help you go right-click and kill it?
My usual scenario is (limited experience however): look at something in the debugger (on a healthy process) and close the window (i.e. full termination is appropriate and I'd even say preferable). If something goes wrong - then I'd welcome a hint there are options - thus the proper Abandon button - what do you think?
Hm, I would rather say that closing the window should have the same effect as pressing "abandon" - personally, I am very much used to this and we do not really have good options to communicate a different behavior of the close button. I think presenting the new behaviors Kill (i) and (iii) in some kind of menu would be less confusing. A kill item in the stack trace menu sounds fine for now (even though killing is not actually related to the selected context). In the long term, I would like to introduce menu buttons in the fashion of [1] in the Morphic UI (Marcel is currently reviewing my proposal for this), then we could move Kill into the Abandon menu, but for now, your proposal sounds perfectly fine to me. :-)
look at something in the debugger (on a healthy process) and close the window (i.e. full termination is appropriate and I'd even say preferable).
Can you help me please, if the process is healthy (so no unwind context is lying on the stack), would there be any difference between the different terminate modi (ii) and (iii)?
But I don't have any good idea for version (ii) yet. Call it #abandon like in the debugger? Then again, #abandon is rather a verb from the Morphic language. Further possible vocables (according to my synonym thesaurus) include #end, #stop, #finish, #unwind, #abort, #exit. Please help... :-)
I'd probably go with something like #terminateLight because it's a proper process termination including unwinds except the ones currently in progress
- so it is a light version of #terminate :) I've checked VisualWorks: they
chose #terminateUnsafely for this type of termination which I don't like much, it sounds too negative; the real meaning is rather #terminateAsSafelyAsPossibleGivenTheCircumstances ;).
Hm, "light" reminds me of "friendly" and (iii) would certainly be more friendly (giving the process more freedom for cleaning things up). Difficult ... What about #terminateNow for (ii) and #terminate for (iii), slightly related to #updateStyle and #updateStyleNow? Since (iii) might continue with the execution of the current halfway-executed context, (ii) is more urgent. Another option would be "terminate: aggressive", analogously to "cleanup: aggressive" on Behavior where #terminate would fall back to "terminate: false". But this would make the new mode a little bit worse accessible.
Concerning your #unwindTo: issue - not sure if this actually related to this thread? Actually I've lost the overview which terminate mode - (ii) or (iii) - you are referring to, maybe we should discuss this in a separate conversation. :-)
TLDR: Concerning our next steps - consider this a small to-do list:
- [ ] We want to implement three modi of process termination:
- [ ] (i) is not yet implemented but should be easy to implement - [ ] (ii) is in my proposal from [2], maybe needs further refinement - [ ] (iii) is pretty much what we have in Process >> #terminate in the current Trunk. Is there anything still missing yet?
- [ ] Decide on names for (ii) and (iii) - see above
- [ ] Decide on UI integration into debugger - see above
Further answers following soon.
Best, Christoph
[1] https://user-images.githubusercontent.com/48567372/60924070-4472fe80-a26e-11... [2] http://lists.squeakfoundation.org/pipermail/squeak-dev/2021-May/215608.html (fix-Process-terminate.cs)
Sent from Squeak Inbox Talk
On 2021-05-29T14:31:13-05:00, m at jaromir.net wrote:
Hi Christoph,
Jaromir, your proposal to provide multiple selectors for modeling separate modes of termination sounds like a very good idea to me. But how many different modes do we actually need? So far I can count three modes:
(i) run no unwind contexts (harshest possible way; currently only achievable by doing "suspendedContext privSender: nil" prior to terminating) (ii) run not-yet started unwind contexts (this is what I proposed in fix-Process-terminate.1.cs [1]) (iii) run all unwind contexts, including those that already have been started (this is the most friendly way that you implemented in #terminate recently)
I think this is it.
Litereally minutes ago had to use privSender: nil to get rid of a debugger :) Fully terminate really is too strong to recover from fatal errors.
... my point here is: Proceeding from an error almost always doesn't seem "right". :-) It is always a decision by the debugging programmer to override the default control flow and switch to the "next plausible alternative control flow", i.e., resume as if the error would have never been raised.
yes - I'd add: even an error may quite often be completely benign, like 'Transcript show: 1/0' - possibly a typo so you just may want to Proceed or fully terminate. In case the error damages a whole subsequent chain of events, you're absolutely right a full termination seems a silly option and a light version of terminate may be the most appropriate.
So I fully agree the decision which termination mode it is stays with the user - so I'm all for giving the user the choices you suggested.
\1. Which mode should we use in which situations?
I think this debate could benefit from a few more concrete usage scenarios. I'm just collecting some here (thinking aloud):
- Process Browser: We can provide multiple options in the process menu. - Debugger: I agree with you that Abandon should always run not-yet started unwind contexts but never resume halfway-executed unwind contexts. So this maps to to mode (ii) from above. - Skimming through most senders of #terminate in the image, they often orchestrate helper processes, deal with unhandled errors or timeouts, or do similar stuff - usually they should be very fine with the friendly version of #terminate, i.e. mode (iii) from above. I think. - Regarding option (1), I think you would need it extremely seldom but maybe in situations like when your stack contains a loop, your unwind contexts will cause a recursion/new error, or you deliberately want to prevent any unwind context from running. No objections against adding a small but decent button for this in the debugger. :-)
Would you agree with these behaviors? Maybe you can add further examples to the list?
Yes
Process Browser - the right click menu could provide all options
Debugger - Abandon could be the lightweight version you proposed. Why not have a proper Abandon button for it? The right click menu on a context could offer the Kill option (next to 'peel to first like this'); no button necessary. Now the question is what should be under the "window close" red-circle-x - heavyweight terminate? I'm thinking this scenario: if the debugger returns after closing the window you start thinking what happened and use Abandon; if it still doesn't help you go right-click and kill it?
My usual scenario is (limited experience however): look at something in the debugger (on a healthy process) and close the window (i.e. full termination is appropriate and I'd even say preferable). If something goes wrong - then I'd welcome a hint there are options - thus the proper Abandon button - what do you think?
\2. How should we name them?
Direct proposal: (i) #kill and (iii) #terminate. After looking up the original behavior of #terminate in Squeak 5.3, I think it would be consistent to resume all halfway-executed unwind contexts in this method. So yes, I also withdraw my criticism about #testNestedUnwind. :-)
But I don't have any good idea for version (ii) yet. Call it #abandon like in the debugger? Then again, #abandon is rather a verb from the Morphic language. Further possible vocables (according to my synonym thesaurus) include #end, #stop, #finish, #unwind, #abort, #exit. Please help... :-)
I'd probably go with something like #terminateLight because it's a proper process termination including unwinds except the ones currently in progress
- so it is a light version of #terminate :) I've checked VisualWorks: they
chose #terminateUnsafely for this type of termination which I don't like much, it sounds too negative; the real meaning is rather #terminateAsSafelyAsPossibleGivenTheCircumstances ;).
I'm wondering whether #unwindTo: (used ony by Generator) is bugged (with regard to dealing with non-local returns), and could be fixed/unified with your approach. Look at these examples:
p := [[Processor activeProcess suspend] valueUninterruptably] fork. Processor yield. p suspendedContext unwindTo: nil
or
p := [[:exit | [Processor activeProcess suspend] ensure: [exit value]] valueWithExit] fork. Processor yield. p suspendedContext unwindTo: nil
If you do `p terminate` instead of `p suspendedContext unwindTo: nil`, it works fine, but #unwindTo causes a block cannot return error - I think it's the same bug all over again :) #value evaluates the non-local return on the wrong stack...
Regarding our cannot return discussion - I have to think about it and I'll post my reply later in [1] to keep it separate :)
Thanks again and regards,
[1] http://forum.world.st/The-Inbox-Kernel-ct-1405-mcz-td5129706.html#a5130114
^[^ Jaromir
Sent from: http://forum.world.st/Squeak-Dev-f45488.html
Hi Christoph,
Once again, apologies for the huge delay in my responding :)
Hm, I would rather say that closing the window should have the same effect as pressing "abandon" - personally, I am very much used to this and we do not really have good options to communicate a different behavior of the close button.
Yes, you're right indeed.
A kill item in the stack trace menu sounds fine for now (even though killing is not actually related to the selected context). In the long term, I would like to introduce menu buttons in the fashion of [1] in the Morphic UI.
Your Abandon button with options is terrific, please disregard my suggestion :)
look at something in the debugger (on a healthy process) and close the window (i.e. full termination is appropriate and I'd even say preferable).
Can you help me please, if the process is healthy (so no unwind context is lying on the stack), would there be any difference between the different terminate modi (ii) and (iii)?
I thought there was a difference, e.g. if you debug #ensure on a healthy process and want to abandon the debugger in the middle - does it make sense?
Example: [^2] ensure: [Transcript cr; show: 'done'. "do some cleaning"]
Now, you start a debugger, do a few steps and in the middle of the ensure block you decide you've seen enough and hit Abandon - if it's implemented with (ii), i.e. the 'aggressive', less safe version of terminate, the block won't get completed. If it contained some cleaning procedures, the cleaning wouldn't be executed... That's why I'm still leaning slightly towards full termination as a default Abandon action - but e.g. Visual Works use the less safe termination in this case, as you suggest. Other Smalltalks I checked (Cuis, Pharo and VA) don't have three ways to terminate at all (yet) :) However, ultimately users that use Squeak the most should have the final say. I'll be happy with both options. (Or make it a preference?).
[[x1 := nil] ensure: [x1 := (2 / 0)]] ensure: [self inform: x1 asString].
Hmm, this example of your nicely justifies your approach: Abandon = (ii) :D
Naming:
Hm, "light" reminds me of "friendly" and (iii) would certainly be more friendly (giving the process more freedom for cleaning things up). Difficult ... What about #terminateNow for (ii) and #terminate for (iii), slightly related to #updateStyle and #updateStyleNow? Since (iii) might continue with the execution of the current halfway-executed context, (ii) is more urgent. Another option would be "terminate: aggressive", analogously to "cleanup: aggressive" on Behavior where #terminate would fall back to "terminate: false". But this would make the new mode a little bit worse accessible.
Agreed, "light" is not good. Checked Visual Works again and they use terminate for (iii), terminateUnsafely for (ii) and terminateUnsafelyNow for kill (i). #teminateNow suggests it would terminate now as opposed to later :) No hint about the nature of the procedure that will attempt to terminate with increased level of possible damage to the environment... But we're getting closer and again, I'll be happy with either of them :)
Here are some ideas for better names:
(i) #destroy (as opposed to #kill, this sounds more like the cheap and unreactable operation it is) (ii) #terminateAggressively, which redirects to #terminate: true (iii) #terminate, which redirects to #terminate: false
One more naming suggestion: (iii) terminate (~run all unwind blocks) (ii) terminateLessSafely (~skip unwind blocks in progress, run all not-yet-started ones) (i) terminateUnsafely (~kill, run no unwinds)
Marcel wrote:
How would you summarize the differences between #terminate, #terminateAggressively, and #kill, each with 1-2 sentences? :-)
Inspired by VW:
(iii) "Terminate the process and allow all unwind blocks to run, even if they are currently in progress." This is the behavior we expect when terminating a healthy process.
(ii) "Terminate the process and run all not-yet-started unwind blocks; if the process is in the middle of an unwind block, then that unwind block will not be completed, but subsequent unwind blocks will be run." This is the behavior we expect when we close the debugger in a situation when the unwind block may not be able to complete, either because of an error, or because it is hung. In most other circumstances, we would want to use #terminate, which allows unwind blocks in progress to complete.
(i) "Terminate the process without attempting to run any of its unwind blocks." This is the behavior we might need in very rare circumstances when modes (iii) and (ii) fail.
Changeset:
Please find the attached changeset in which I have changed the names accordingly and also have removed the kill/destroy operation from the UI as per request of Marcel. I aggree with his argument that destroy is a kind of private operation that affects the interna of the process and should be only used for troubleshooting, so we should not give this hack too much visibility.
Nice! I liked your Abandon button with options more but this is a start. Kill option: do you mean no access to the kill option from the Process browser either? Process browser is a very troubleshooting tool :)
And finally, If I may - I'd slightly prefer not adding another #terminate: to the Process class only to serve a single sender but rather inline it and address the two termination modes directly in the ProcessBrowser class:
ProcessBrowser class >> #terminateProcess: aProcess aggressively: aggressive
aProcess ifNil: [^ self]. self suspendedProcesses removeKey: aProcess ifAbsent: []. - aProcess terminate: aggressive. + aggressive ifFalse: [aProcess terminate] ifTrue: [aProcess terminateAggressively]
Concerning your #unwindTo: issue - not sure if this actually related to this thread? Actually I've lost the overview which terminate mode - (ii) or (iii) - you are referring to, maybe we should discuss this in a separate conversation. :-)
Yes, it's a bug and deserves its own thread.
=== Summary:
TLDR: Concerning our next steps - consider this a small to-do list:
- [ ] We want to implement three modi of process termination:
- [x] (i) is not yet implemented but should be easy to implement
- [x] (ii) is in my proposal from [2], maybe needs further refinement
- [ ] (iii) is pretty much what we have in Process >> #terminate in the current Trunk. Is there anything still missing yet?
- [ ] Decide on names for (ii) and (iii) - see above
- [ ] Decide on UI integration into debugger - see above
Add this one? - [ ] Decide on UI integration into Process Browser
Regarding Process >> #terminate - the current Trunk version is not final; the final version is the Inbox's Kernel-jar.1414.
More or less done! :)
Thanks,
Jaromir
On 2021-08-30T12:51:21+02:00, christoph.thiede@student.hpi.uni-potsdam.de wrote:
Hi Marcel, hi all,
How would you summarize the differences between #terminate, #terminateAggressively, and #kill, each with 1-2 sentences? :-)
Let's keep using the numbers from above instead, as the names are only my imperfect proposals:
(i) run no unwind contexts (harshest possible way; currently only achievable by doing "suspendedContext privSender: nil" prior to terminating). You would need this very rarely, if at all, for troubleshooting. (ii) run not-yet started unwind contexts (this is what I proposed in fix-Process-terminate.1.cs [1]). This is the behavior we expect from the Abandon button in a debugger. I think? (iii) run all unwind contexts, including those that already have been started (this is the most friendly way that you implemented in #terminate recently). This is the behavior we expect when terminating a healthy process.
Here are some ideas for better names:
(i) #destroy (as opposed to #kill, this sounds more like the cheap and unreactable operation it is) (ii) #terminateAggressively, which redirects to #terminate: true (iii) #terminate, which redirects to #terminate: false
Please find the attached changeset in which I have changed the names accordingly and also have removed the kill/destroy operation from the UI as per request of Marcel. I aggree with his argument that destroy is a kind of private operation that affects the interna of the process and should be only used for troubleshooting, so we should not give this hack too much visibility.
Best, Christoph
Sent from Squeak Inbox Talk
On 2021-08-23T13:59:04+02:00, marcel.taeumel at hpi.de wrote:
Hi Christoph --
How would you summarize the differences between #terminate, #terminateAggressively, and #kill, each with 1-2 sentences? :-)
Best, Marcel Am 23.08.2021 13:18:40 schrieb christoph.thiede at student.hpi.uni-potsdam.de <christoph.thiede at student.hpi.uni-potsdam.de>: Hi all, hi Jaromir,
based on the to-do list from my previous message, I have created a changeset that adds Process >> #terminateAggressively (ii) and Process >> #kill (i) and integrates them into the process browser and the debugger. Most importantly, the following snippet will inform "nil" now instead of "ZeroDevision" after abandoning the error:
����[[x1 := nil] ensure: [x1 := (2 / 0)]] ��������ensure: [self inform: x1 asString].
With this changeset, I would be fully happy again with the functionality of the debugger. :-) What remains to do is the following:
- [ ] We want to implement three modi of process termination:
- [x] (i) - see #kill
- [X] (ii) - see #terminateAggressively
- [ ] (iii) is pretty much what we have in Process >> #terminate in the current Trunk. Is there anything still missing yet?
- [ ] Decide on names for (ii) and (iii) - see my previous message
- [ ] Decide on UI integration into debugger - proposal exists in Process-terminateAggressively.1, but open for criticism
- [ ] Test #terminateAggressively (actualy, I don't have a good of overview of all your new #terminate tests - probably it would be easiest to wait until they have arrived in the Trunk and then create copies of them to test #terminateAggressively analogously :D)
Best, Christoph
Sent from Squeak Inbox Talk [https://github.com/hpi-swa-lab/squeak-inbox-talk]
On 2021-08-22T16:07:54+02:00, christoph.thiede at student.hpi.uni-potsdam.de wrote:
Hi Jaromir,
back from vacation, *finally* I have found some time to return back to this pleasant problem! :-)
Great, it sounds like we're already agreeing pretty much, just let me address some small points:
Debugger - Abandon could be the lightweight version you proposed. Why not have a proper Abandon button for it? ����The right click menu on a context could offer the Kill option (next to 'peel to first like this'); no button necessary. Now the question is what should be under the "window close" red-circle-x - heavyweight terminate? I'm thinking this scenario: if the debugger returns after closing the window you start thinking what happened and use Abandon; if it still doesn't help you go right-click and kill it?
My usual scenario is (limited experience however): look at something in the debugger (on a healthy process) and close the window (i.e. full termination is appropriate and I'd even say preferable). If something goes wrong - then I'd welcome a hint there are options - thus the proper Abandon button - what do you think?
Hm, I would rather say that closing the window should have the same effect as pressing "abandon" - personally, I am very much used to this and we do not really have good options to communicate a different behavior of the close button. I think presenting the new behaviors Kill (i) and (iii) in some kind of menu would be less confusing. A kill item in the stack trace menu sounds fine for now (even though killing is not actually related to the selected context). In the long term, I would like to introduce menu buttons in the fashion of [1] in the Morphic UI (Marcel is currently reviewing my proposal for this), then we could move Kill into the Abandon menu, but for now, your proposal sounds perfectly fine to me. :-)
look at something in the debugger (on a healthy process) and close the window (i.e. full termination is appropriate and I'd even say preferable).
Can you help me please, if the process is healthy (so no unwind context is lying on the stack), would there be any difference between the different terminate modi (ii) and (iii)?
But I don't have any good idea for version (ii) yet. Call it #abandon like in the debugger? Then again, #abandon is rather a verb from the Morphic language. Further possible vocables (according to my synonym thesaurus) include #end, #stop, #finish, #unwind, #abort, #exit. Please help... :-)
I'd probably go with something like #terminateLight because it's a proper process termination including unwinds except the ones currently in progress
- so it is a light version of #terminate :) I've checked VisualWorks: they
chose #terminateUnsafely for this type of termination which I don't like much, it sounds too negative; the real meaning is rather #terminateAsSafelyAsPossibleGivenTheCircumstances ;).
Hm, "light" reminds me of "friendly" and (iii) would certainly be more friendly (giving the process more freedom for cleaning things up). Difficult ... What about #terminateNow for (ii) and #terminate for (iii), slightly related to #updateStyle and #updateStyleNow? Since (iii) might continue with the execution of the current halfway-executed context, (ii) is more urgent. Another option would be "terminate: aggressive", analogously to "cleanup: aggressive" on Behavior where #terminate would fall back to "terminate: false". But this would make the new mode a little bit worse accessible.
Concerning your #unwindTo: issue - not sure if this actually related to this thread? Actually I've lost the overview which terminate mode - (ii) or (iii) - you are referring to, maybe we should discuss this in a separate conversation. :-)
TLDR: Concerning our next steps - consider this a small to-do list:
- [ ] We want to implement three modi of process termination:
����- [ ] (i) is not yet implemented but should be easy to implement ����- [ ] (ii) is in my proposal from [2], maybe needs further refinement ����- [ ] (iii) is pretty much what we have in Process >> #terminate in the current Trunk. Is there anything still missing yet?
- [ ] Decide on names for (ii) and (iii) - see above
- [ ] Decide on UI integration into debugger - see above
Further answers following soon.
Best, Christoph
[1] https://user-images.githubusercontent.com/48567372/60924070-4472fe80-a26e-11... [2] http://lists.squeakfoundation.org/pipermail/squeak-dev/2021-May/215608.html (fix-Process-terminate.cs)
Sent from Squeak Inbox Talk
On 2021-05-29T14:31:13-05:00, m at jaromir.net wrote:
Hi Christoph,
Jaromir, your proposal to provide multiple selectors for modeling separate modes of termination sounds like a very good idea to me. But how many different modes do we actually need? So far I can count three modes:
(i) run no unwind contexts (harshest possible way; currently only achievable by doing "suspendedContext privSender: nil" prior to terminating) (ii) run not-yet started unwind contexts (this is what I proposed in fix-Process-terminate.1.cs [1]) (iii) run all unwind contexts, including those that already have been started (this is the most friendly way that you implemented in #terminate recently)
I think this is it.
Litereally minutes ago had to use privSender: nil to get rid of a debugger :) Fully terminate really is too strong to recover from fatal errors.
... my point here is: Proceeding from an error almost always doesn't seem "right". :-) It is always a decision by the debugging programmer to override the default control flow and switch to the "next plausible alternative control flow", i.e., resume as if the error would have never been raised.
yes - I'd add: even an error may quite often be completely benign, like 'Transcript show: 1/0' - possibly a typo so you just may want to Proceed or fully terminate. In case the error damages a whole subsequent chain of events, you're absolutely right a full termination seems a silly option and a light version of terminate may be the most appropriate.
So I fully agree the decision which termination mode it is stays with the user - so I'm all for giving the user the choices you suggested.
\1. Which mode should we use in which situations?
I think this debate could benefit from a few more concrete usage scenarios. I'm just collecting some here (thinking aloud):
- Process Browser: We can provide multiple options in the process menu. - Debugger: I agree with you that Abandon should always run not-yet started unwind contexts but never resume halfway-executed unwind contexts. So this maps to to mode (ii) from above. - Skimming through most senders of #terminate in the image, they often orchestrate helper processes, deal with unhandled errors or timeouts, or do similar stuff - usually they should be very fine with the friendly version of #terminate, i.e. mode (iii) from above. I think. - Regarding option (1), I think you would need it extremely seldom but maybe in situations like when your stack contains a loop, your unwind contexts will cause a recursion/new error, or you deliberately want to prevent any unwind context from running. No objections against adding a small but decent button for this in the debugger. :-)
Would you agree with these behaviors? Maybe you can add further examples to the list?
Yes
Process Browser - the right click menu could provide all options
Debugger - Abandon could be the lightweight version you proposed. Why not have a proper Abandon button for it? ����The right click menu on a context could offer the Kill option (next to 'peel to first like this'); no button necessary. ����Now the question is what should be under the "window close" red-circle-x - heavyweight terminate? I'm thinking this scenario: if the debugger returns after closing the window you start thinking what happened and use Abandon; if it still doesn't help you go right-click and kill it?
My usual scenario is (limited experience however): look at something in the debugger (on a healthy process) and close the window (i.e. full termination is appropriate and I'd even say preferable). If something goes wrong - then I'd welcome a hint there are options - thus the proper Abandon button - what do you think?
\2. How should we name them?
Direct proposal: (i) #kill and (iii) #terminate. After looking up the original behavior of #terminate in Squeak 5.3, I think it would be consistent to resume all halfway-executed unwind contexts in this method. So yes, I also withdraw my criticism about #testNestedUnwind. :-)
But I don't have any good idea for version (ii) yet. Call it #abandon like in the debugger? Then again, #abandon is rather a verb from the Morphic language. Further possible vocables (according to my synonym thesaurus) include #end, #stop, #finish, #unwind, #abort, #exit. Please help... :-)
I'd probably go with something like #terminateLight because it's a proper process termination including unwinds except the ones currently in progress
- so it is a light version of #terminate :) I've checked VisualWorks: they
chose #terminateUnsafely for this type of termination which I don't like much, it sounds too negative; the real meaning is rather #terminateAsSafelyAsPossibleGivenTheCircumstances ;).
I'm wondering whether #unwindTo: (used ony by Generator) is bugged (with regard to dealing with non-local returns), and could be fixed/unified with your approach. Look at these examples:
p := [[Processor activeProcess suspend] valueUninterruptably] fork. Processor yield. p suspendedContext unwindTo: nil
or
p := [[:exit | [Processor activeProcess suspend] ensure: [exit value]] valueWithExit] fork. Processor yield. p suspendedContext unwindTo: nil
If you do `p terminate` instead of `p suspendedContext unwindTo: nil`, it works fine, but #unwindTo causes a block cannot return error - I think it's the same bug all over again :) #value evaluates the non-local return on the wrong stack...
Regarding our cannot return discussion - I have to think about it and I'll post my reply later in [1] to keep it separate :)
Thanks again and regards,
[1] http://forum.world.st/The-Inbox-Kernel-ct-1405-mcz-td5129706.html#a5130114
^[^ Jaromir
Sent from: http://forum.world.st/Squeak-Dev-f45488.html
Hi Jaromir, Hi Marcel, Hi Eliot, Hi all,
Once again, apologies for the huge delay in my responding :)
The apologies are all mine. :)
I just have uploaded a revised version of Process-terminateAggressively.2.cs to the Trunk! :-) It includes #terminateAggressive & #destroy, the connection to the debugger/process browser, UI & E2E tests, and a bit of documentation. Please see the version message of Kernel-ct.1434 for more details.
Once again, thank you - and Marcel - for all the helpful comments! Of course, everything is subject to further discussion if you dislike something or - unthinkable! - find a bug, but at least the prominent example of a debugger continuing after pressing Abandon should be eliminated by now.
I thought there was a difference, e.g. if you debug #ensure on a healthy process and want to abandon the debugger in the middle - does it make sense?
You're totally right, I forgot about the non-local returns again. :) Anyway, as discussed earlier, I think (and Marcel aggreed) that it would be a more valid assumption in the debugger not to resume an active handler context after pressing Abandon ...
[[x1 := nil] ensure: [x1 := (2 / 0)]]
ensure: [self inform: x1 asString].
Hmm, this example of your nicely justifies your approach: Abandon = (ii) :D
This. :D
Marcel wrote:
How would you summarize the differences between #terminate, #terminateAggressively, and #kill, each with 1-2 sentences? :-)
Inspired by VW:
Thanks for your helpstrings, they served as a good base for the comments in the new termination methods. :-)
And finally, If I may - I'd slightly prefer not adding another #terminate: to the Process class only to serve a single sender but rather inline it and address the two termination modes directly in the ProcessBrowser class:
Aggreed and applied.
Updated summary:
- [x] We want to implement three modi of process termination: - [x] (i) - #destroy - [x] (ii) - #terminateAggressively - [ ] (iii) - #terminate, I still need to review your fixes - [x] Decide on names for (ii) and (iii) - done provided no objections - [x] Decide on UI integration into debugger - done provided no objections - [x] Decide on UI integration into Process Browser - done provided no objections
And just for some orientation, the other remaining issues:
- [ ] open #terminate issues (I still need to review them) - [ ] proceedable BlockCannotReturn (I still need to review them?) - [ ] Tackling Context>>#runUntilErrorReturnFrom: (most likely also my turn) - [ ] issue with #unwindTo: - see http://lists.squeakfoundation.org/pipermail/squeak-dev/2021-May/215758.html#... (regression tests & patches welcome :))
Some by-the-way notes:
- I have merged some of your process tests earlier today. Thank you for providing them!
- I keep seeing some unexpected 'infinite recursion in doesNotUnderstand:' from time to time but could not reproduce them. Just FYI, afaik you and Eliot have concocted this - I still need to read up all the details in my overflowing inbox. ;-)
Further replies, reviews, and merges coming soon(TM)! :-)
Best, Christoph
--- Sent from Squeak Inbox Talk
On 2021-11-15T18:33:33+01:00, mail@jaromir.net wrote:
Hi Christoph,
Once again, apologies for the huge delay in my responding :)
Hm, I would rather say that closing the window should have the same effect as pressing "abandon" - personally, I am very much used to this and we do not really have good options to communicate a different behavior of the close button.
Yes, you're right indeed.
A kill item in the stack trace menu sounds fine for now (even though killing is not actually related to the selected context). In the long term, I would like to introduce menu buttons in the fashion of [1] in the Morphic UI.
Your Abandon button with options is terrific, please disregard my suggestion :)
look at something in the debugger (on a healthy process) and close the window (i.e. full termination is appropriate and I'd even say preferable).
Can you help me please, if the process is healthy (so no unwind context is lying on the stack), would there be any difference between the different terminate modi (ii) and (iii)?
I thought there was a difference, e.g. if you debug #ensure on a healthy process and want to abandon the debugger in the middle - does it make sense?
Example: [^2] ensure: [Transcript cr; show: 'done'. "do some cleaning"]
Now, you start a debugger, do a few steps and in the middle of the ensure block you decide you've seen enough and hit Abandon - if it's implemented with (ii), i.e. the 'aggressive', less safe version of terminate, the block won't get completed. If it contained some cleaning procedures, the cleaning wouldn't be executed... That's why I'm still leaning slightly towards full termination as a default Abandon action - but e.g. Visual Works use the less safe termination in this case, as you suggest. Other Smalltalks I checked (Cuis, Pharo and VA) don't have three ways to terminate at all (yet) :) However, ultimately users that use Squeak the most should have the final say. I'll be happy with both options. (Or make it a preference?).
[[x1 := nil] ensure: [x1 := (2 / 0)]] ensure: [self inform: x1 asString].
Hmm, this example of your nicely justifies your approach: Abandon = (ii) :D
Naming:
Hm, "light" reminds me of "friendly" and (iii) would certainly be more friendly (giving the process more freedom for cleaning things up). Difficult ... What about #terminateNow for (ii) and #terminate for (iii), slightly related to #updateStyle and #updateStyleNow? Since (iii) might continue with the execution of the current halfway-executed context, (ii) is more urgent. Another option would be "terminate: aggressive", analogously to "cleanup: aggressive" on Behavior where #terminate would fall back to "terminate: false". But this would make the new mode a little bit worse accessible.
Agreed, "light" is not good. Checked Visual Works again and they use terminate for (iii), terminateUnsafely for (ii) and terminateUnsafelyNow for kill (i). #teminateNow suggests it would terminate now as opposed to later :) No hint about the nature of the procedure that will attempt to terminate with increased level of possible damage to the environment... But we're getting closer and again, I'll be happy with either of them :)
Here are some ideas for better names:
(i) #destroy (as opposed to #kill, this sounds more like the cheap and unreactable operation it is) (ii) #terminateAggressively, which redirects to #terminate: true (iii) #terminate, which redirects to #terminate: false
One more naming suggestion: (iii) terminate (~run all unwind blocks) (ii) terminateLessSafely (~skip unwind blocks in progress, run all not-yet-started ones) (i) terminateUnsafely (~kill, run no unwinds)
Marcel wrote:
How would you summarize the differences between #terminate, #terminateAggressively, and #kill, each with 1-2 sentences? :-)
Inspired by VW:
(iii) "Terminate the process and allow all unwind blocks to run, even if they are currently in progress." This is the behavior we expect when terminating a healthy process.
(ii) "Terminate the process and run all not-yet-started unwind blocks; if the process is in the middle of an unwind block, then that unwind block will not be completed, but subsequent unwind blocks will be run." This is the behavior we expect when we close the debugger in a situation when the unwind block may not be able to complete, either because of an error, or because it is hung. In most other circumstances, we would want to use #terminate, which allows unwind blocks in progress to complete.
(i) "Terminate the process without attempting to run any of its unwind blocks." This is the behavior we might need in very rare circumstances when modes (iii) and (ii) fail.
Changeset:
Please find the attached changeset in which I have changed the names accordingly and also have removed the kill/destroy operation from the UI as per request of Marcel. I aggree with his argument that destroy is a kind of private operation that affects the interna of the process and should be only used for troubleshooting, so we should not give this hack too much visibility.
Nice! I liked your Abandon button with options more but this is a start. Kill option: do you mean no access to the kill option from the Process browser either? Process browser is a very troubleshooting tool :)
And finally, If I may - I'd slightly prefer not adding another #terminate: to the Process class only to serve a single sender but rather inline it and address the two termination modes directly in the ProcessBrowser class:
ProcessBrowser class >> #terminateProcess: aProcess aggressively: aggressive
aProcess ifNil: [^ self]. self suspendedProcesses removeKey: aProcess ifAbsent: []. - aProcess terminate: aggressive. + aggressive ifFalse: [aProcess terminate] ifTrue: [aProcess terminateAggressively]
Concerning your #unwindTo: issue - not sure if this actually related to this thread? Actually I've lost the overview which terminate mode - (ii) or (iii) - you are referring to, maybe we should discuss this in a separate conversation. :-)
Yes, it's a bug and deserves its own thread.
=== Summary:
TLDR: Concerning our next steps - consider this a small to-do list:
- [ ] We want to implement three modi of process termination:
- [x] (i) is not yet implemented but should be easy to implement
- [x] (ii) is in my proposal from [2], maybe needs further refinement
- [ ] (iii) is pretty much what we have in Process >> #terminate in the current Trunk. Is there anything still missing yet?
- [ ] Decide on names for (ii) and (iii) - see above
- [ ] Decide on UI integration into debugger - see above
Add this one?
- [ ] Decide on UI integration into Process Browser
Regarding Process >> #terminate - the current Trunk version is not final; the final version is the Inbox's Kernel-jar.1414.
More or less done! :)
Thanks,
Jaromir
On 2021-08-30T12:51:21+02:00, christoph.thiede at student.hpi.uni-potsdam.de wrote:
Hi Marcel, hi all,
How would you summarize the differences between #terminate, #terminateAggressively, and #kill, each with 1-2 sentences? :-)
Let's keep using the numbers from above instead, as the names are only my imperfect proposals:
(i) run no unwind contexts (harshest possible way; currently only achievable by doing "suspendedContext privSender: nil" prior to terminating). You would need this very rarely, if at all, for troubleshooting. (ii) run not-yet started unwind contexts (this is what I proposed in fix-Process-terminate.1.cs [1]). This is the behavior we expect from the Abandon button in a debugger. I think? (iii) run all unwind contexts, including those that already have been started (this is the most friendly way that you implemented in #terminate recently). This is the behavior we expect when terminating a healthy process.
Here are some ideas for better names:
(i) #destroy (as opposed to #kill, this sounds more like the cheap and unreactable operation it is) (ii) #terminateAggressively, which redirects to #terminate: true (iii) #terminate, which redirects to #terminate: false
Please find the attached changeset in which I have changed the names accordingly and also have removed the kill/destroy operation from the UI as per request of Marcel. I aggree with his argument that destroy is a kind of private operation that affects the interna of the process and should be only used for troubleshooting, so we should not give this hack too much visibility.
Best, Christoph
Sent from Squeak Inbox Talk
On 2021-08-23T13:59:04+02:00, marcel.taeumel at hpi.de wrote:
Hi Christoph --
How would you summarize the differences between #terminate, #terminateAggressively, and #kill, each with 1-2 sentences? :-)
Best, Marcel Am 23.08.2021 13:18:40 schrieb christoph.thiede at student.hpi.uni-potsdam.de <christoph.thiede at student.hpi.uni-potsdam.de>: Hi all, hi Jaromir,
based on the to-do list from my previous message, I have created a changeset that adds Process >> #terminateAggressively (ii) and Process >> #kill (i) and integrates them into the process browser and the debugger. Most importantly, the following snippet will inform "nil" now instead of "ZeroDevision" after abandoning the error:
????[[x1 := nil] ensure: [x1 := (2 / 0)]] ????????ensure: [self inform: x1 asString].
With this changeset, I would be fully happy again with the functionality of the debugger. :-) What remains to do is the following:
- [ ] We want to implement three modi of process termination:
- [x] (i) - see #kill
- [X] (ii) - see #terminateAggressively
- [ ] (iii) is pretty much what we have in Process >> #terminate in the current Trunk. Is there anything still missing yet?
- [ ] Decide on names for (ii) and (iii) - see my previous message
- [ ] Decide on UI integration into debugger - proposal exists in Process-terminateAggressively.1, but open for criticism
- [ ] Test #terminateAggressively (actualy, I don't have a good of overview of all your new #terminate tests - probably it would be easiest to wait until they have arrived in the Trunk and then create copies of them to test #terminateAggressively analogously :D)
Best, Christoph
Sent from Squeak Inbox Talk [https://github.com/hpi-swa-lab/squeak-inbox-talk]
On 2021-08-22T16:07:54+02:00, christoph.thiede at student.hpi.uni-potsdam.de wrote:
Hi Jaromir,
back from vacation, *finally* I have found some time to return back to this pleasant problem! :-)
Great, it sounds like we're already agreeing pretty much, just let me address some small points:
Debugger - Abandon could be the lightweight version you proposed. Why not have a proper Abandon button for it? ????The right click menu on a context could offer the Kill option (next to 'peel to first like this'); no button necessary. Now the question is what should be under the "window close" red-circle-x - heavyweight terminate? I'm thinking this scenario: if the debugger returns after closing the window you start thinking what happened and use Abandon; if it still doesn't help you go right-click and kill it?
My usual scenario is (limited experience however): look at something in the debugger (on a healthy process) and close the window (i.e. full termination is appropriate and I'd even say preferable). If something goes wrong - then I'd welcome a hint there are options - thus the proper Abandon button - what do you think?
Hm, I would rather say that closing the window should have the same effect as pressing "abandon" - personally, I am very much used to this and we do not really have good options to communicate a different behavior of the close button. I think presenting the new behaviors Kill (i) and (iii) in some kind of menu would be less confusing. A kill item in the stack trace menu sounds fine for now (even though killing is not actually related to the selected context). In the long term, I would like to introduce menu buttons in the fashion of [1] in the Morphic UI (Marcel is currently reviewing my proposal for this), then we could move Kill into the Abandon menu, but for now, your proposal sounds perfectly fine to me. :-)
look at something in the debugger (on a healthy process) and close the window (i.e. full termination is appropriate and I'd even say preferable).
Can you help me please, if the process is healthy (so no unwind context is lying on the stack), would there be any difference between the different terminate modi (ii) and (iii)?
But I don't have any good idea for version (ii) yet. Call it #abandon like in the debugger? Then again, #abandon is rather a verb from the Morphic language. Further possible vocables (according to my synonym thesaurus) include #end, #stop, #finish, #unwind, #abort, #exit. Please help... :-)
I'd probably go with something like #terminateLight because it's a proper process termination including unwinds except the ones currently in progress
- so it is a light version of #terminate :) I've checked VisualWorks: they
chose #terminateUnsafely for this type of termination which I don't like much, it sounds too negative; the real meaning is rather #terminateAsSafelyAsPossibleGivenTheCircumstances ;).
Hm, "light" reminds me of "friendly" and (iii) would certainly be more friendly (giving the process more freedom for cleaning things up). Difficult ... What about #terminateNow for (ii) and #terminate for (iii), slightly related to #updateStyle and #updateStyleNow? Since (iii) might continue with the execution of the current halfway-executed context, (ii) is more urgent. Another option would be "terminate: aggressive", analogously to "cleanup: aggressive" on Behavior where #terminate would fall back to "terminate: false". But this would make the new mode a little bit worse accessible.
Concerning your #unwindTo: issue - not sure if this actually related to this thread? Actually I've lost the overview which terminate mode - (ii) or (iii) - you are referring to, maybe we should discuss this in a separate conversation. :-)
TLDR: Concerning our next steps - consider this a small to-do list:
- [ ] We want to implement three modi of process termination:
????- [ ] (i) is not yet implemented but should be easy to implement ????- [ ] (ii) is in my proposal from [2], maybe needs further refinement ????- [ ] (iii) is pretty much what we have in Process >> #terminate in the current Trunk. Is there anything still missing yet?
- [ ] Decide on names for (ii) and (iii) - see above
- [ ] Decide on UI integration into debugger - see above
Further answers following soon.
Best, Christoph
[1] https://user-images.githubusercontent.com/48567372/60924070-4472fe80-a26e-11... [2] http://lists.squeakfoundation.org/pipermail/squeak-dev/2021-May/215608.html (fix-Process-terminate.cs)
Sent from Squeak Inbox Talk
On 2021-05-29T14:31:13-05:00, m at jaromir.net wrote:
Hi Christoph,
Jaromir, your proposal to provide multiple selectors for modeling separate modes of termination sounds like a very good idea to me. But how many different modes do we actually need? So far I can count three modes:
(i) run no unwind contexts (harshest possible way; currently only achievable by doing "suspendedContext privSender: nil" prior to terminating) (ii) run not-yet started unwind contexts (this is what I proposed in fix-Process-terminate.1.cs [1]) (iii) run all unwind contexts, including those that already have been started (this is the most friendly way that you implemented in #terminate recently)
I think this is it.
Litereally minutes ago had to use privSender: nil to get rid of a debugger :) Fully terminate really is too strong to recover from fatal errors.
... my point here is: Proceeding from an error almost always doesn't seem "right". :-) It is always a decision by the debugging programmer to override the default control flow and switch to the "next plausible alternative control flow", i.e., resume as if the error would have never been raised.
yes - I'd add: even an error may quite often be completely benign, like 'Transcript show: 1/0' - possibly a typo so you just may want to Proceed or fully terminate. In case the error damages a whole subsequent chain of events, you're absolutely right a full termination seems a silly option and a light version of terminate may be the most appropriate.
So I fully agree the decision which termination mode it is stays with the user - so I'm all for giving the user the choices you suggested.
\1. Which mode should we use in which situations?
I think this debate could benefit from a few more concrete usage scenarios. I'm just collecting some here (thinking aloud):
- Process Browser: We can provide multiple options in the process menu. - Debugger: I agree with you that Abandon should always run not-yet started unwind contexts but never resume halfway-executed unwind contexts. So this maps to to mode (ii) from above. - Skimming through most senders of #terminate in the image, they often orchestrate helper processes, deal with unhandled errors or timeouts, or do similar stuff - usually they should be very fine with the friendly version of #terminate, i.e. mode (iii) from above. I think. - Regarding option (1), I think you would need it extremely seldom but maybe in situations like when your stack contains a loop, your unwind contexts will cause a recursion/new error, or you deliberately want to prevent any unwind context from running. No objections against adding a small but decent button for this in the debugger. :-)
Would you agree with these behaviors? Maybe you can add further examples to the list?
Yes
Process Browser - the right click menu could provide all options
Debugger - Abandon could be the lightweight version you proposed. Why not have a proper Abandon button for it? ????The right click menu on a context could offer the Kill option (next to 'peel to first like this'); no button necessary. ????Now the question is what should be under the "window close" red-circle-x - heavyweight terminate? I'm thinking this scenario: if the debugger returns after closing the window you start thinking what happened and use Abandon; if it still doesn't help you go right-click and kill it?
My usual scenario is (limited experience however): look at something in the debugger (on a healthy process) and close the window (i.e. full termination is appropriate and I'd even say preferable). If something goes wrong - then I'd welcome a hint there are options - thus the proper Abandon button - what do you think?
\2. How should we name them?
Direct proposal: (i) #kill and (iii) #terminate. After looking up the original behavior of #terminate in Squeak 5.3, I think it would be consistent to resume all halfway-executed unwind contexts in this method. So yes, I also withdraw my criticism about #testNestedUnwind. :-)
But I don't have any good idea for version (ii) yet. Call it #abandon like in the debugger? Then again, #abandon is rather a verb from the Morphic language. Further possible vocables (according to my synonym thesaurus) include #end, #stop, #finish, #unwind, #abort, #exit. Please help... :-)
I'd probably go with something like #terminateLight because it's a proper process termination including unwinds except the ones currently in progress
- so it is a light version of #terminate :) I've checked VisualWorks: they
chose #terminateUnsafely for this type of termination which I don't like much, it sounds too negative; the real meaning is rather #terminateAsSafelyAsPossibleGivenTheCircumstances ;).
I'm wondering whether #unwindTo: (used ony by Generator) is bugged (with regard to dealing with non-local returns), and could be fixed/unified with your approach. Look at these examples:
p := [[Processor activeProcess suspend] valueUninterruptably] fork. Processor yield. p suspendedContext unwindTo: nil
or
p := [[:exit | [Processor activeProcess suspend] ensure: [exit value]] valueWithExit] fork. Processor yield. p suspendedContext unwindTo: nil
If you do `p terminate` instead of `p suspendedContext unwindTo: nil`, it works fine, but #unwindTo causes a block cannot return error - I think it's the same bug all over again :) #value evaluates the non-local return on the wrong stack...
Regarding our cannot return discussion - I have to think about it and I'll post my reply later in [1] to keep it separate :)
Thanks again and regards,
[1] http://forum.world.st/The-Inbox-Kernel-ct-1405-mcz-td5129706.html#a5130114
^[^ Jaromir
Sent from: http://forum.world.st/Squeak-Dev-f45488.html
Hi Christoph --
Big thanks! Now get some sleep ... ;o)
Best, Marcel Am 16.12.2021 03:11:09 schrieb christoph.thiede@student.hpi.uni-potsdam.de christoph.thiede@student.hpi.uni-potsdam.de: Hi Jaromir, Hi Marcel, Hi Eliot, Hi all,
Once again, apologies for the huge delay in my responding :)
The apologies are all mine. :)
I just have uploaded a revised version of Process-terminateAggressively.2.cs to the Trunk! :-) It includes #terminateAggressive & #destroy, the connection to the debugger/process browser, UI & E2E tests, and a bit of documentation. Please see the version message of Kernel-ct.1434 for more details.
Once again, thank you - and Marcel - for all the helpful comments! Of course, everything is subject to further discussion if you dislike something or - unthinkable! - find a bug, but at least the prominent example of a debugger continuing after pressing Abandon should be eliminated by now.
I thought there was a difference, e.g. if you debug #ensure on a healthy process and want to abandon the debugger in the middle - does it make sense?
You're totally right, I forgot about the non-local returns again. :) Anyway, as discussed earlier, I think (and Marcel aggreed) that it would be a more valid assumption in the debugger not to resume an active handler context after pressing Abandon ...
[[x1 := nil] ensure: [x1 := (2 / 0)]]
ensure: [self inform: x1 asString].
Hmm, this example of your nicely justifies your approach: Abandon = (ii) :D
This. :D
Marcel wrote:
How would you summarize the differences between #terminate, #terminateAggressively, and #kill, each with 1-2 sentences? :-)
Inspired by VW:
Thanks for your helpstrings, they served as a good base for the comments in the new termination methods. :-)
And finally, If I may - I'd slightly prefer not adding another #terminate: to the Process class only to serve a single sender but rather inline it and address the two termination modes directly in the ProcessBrowser class:
Aggreed and applied.
Updated summary:
- [x] We want to implement three modi of process termination: - [x] (i) - #destroy - [x] (ii) - #terminateAggressively - [ ] (iii) - #terminate, I still need to review your fixes - [x] Decide on names for (ii) and (iii) - done provided no objections - [x] Decide on UI integration into debugger - done provided no objections - [x] Decide on UI integration into Process Browser - done provided no objections
And just for some orientation, the other remaining issues:
- [ ] open #terminate issues (I still need to review them) - [ ] proceedable BlockCannotReturn (I still need to review them?) - [ ] Tackling Context>>#runUntilErrorReturnFrom: (most likely also my turn) - [ ] issue with #unwindTo: - see http://lists.squeakfoundation.org/pipermail/squeak-dev/2021-May/215758.html#... (regression tests & patches welcome :))
Some by-the-way notes:
- I have merged some of your process tests earlier today. Thank you for providing them!
- I keep seeing some unexpected 'infinite recursion in doesNotUnderstand:' from time to time but could not reproduce them. Just FYI, afaik you and Eliot have concocted this - I still need to read up all the details in my overflowing inbox. ;-)
Further replies, reviews, and merges coming soon(TM)! :-)
Best, Christoph
--- Sent from Squeak Inbox Talk [https://github.com/hpi-swa-lab/squeak-inbox-talk]
On 2021-11-15T18:33:33+01:00, mail@jaromir.net wrote:
Hi Christoph,
Once again, apologies for the huge delay in my responding :)
Hm, I would rather say that closing the window should have the same effect as pressing "abandon" - personally, I am very much used to this and we do not really have good options to communicate a different behavior of the close button.
Yes, you're right indeed.
A kill item in the stack trace menu sounds fine for now (even though killing is not actually related to the selected context). In the long term, I would like to introduce menu buttons in the fashion of [1] in the Morphic UI.
Your Abandon button with options is terrific, please disregard my suggestion :)
look at something in the debugger (on a healthy process) and close the window (i.e. full termination is appropriate and I'd even say preferable).
Can you help me please, if the process is healthy (so no unwind context is lying on the stack), would there be any difference between the different terminate modi (ii) and (iii)?
I thought there was a difference, e.g. if you debug #ensure on a healthy process and want to abandon the debugger in the middle - does it make sense?
Example: [^2] ensure: [Transcript cr; show: 'done'. "do some cleaning"]
Now, you start a debugger, do a few steps and in the middle of the ensure block you decide you've seen enough and hit Abandon - if it's implemented with (ii), i.e. the 'aggressive', less safe version of terminate, the block won't get completed. If it contained some cleaning procedures, the cleaning wouldn't be executed... That's why I'm still leaning slightly towards full termination as a default Abandon action - but e.g. Visual Works use the less safe termination in this case, as you suggest. Other Smalltalks I checked (Cuis, Pharo and VA) don't have three ways to terminate at all (yet) :) However, ultimately users that use Squeak the most should have the final say. I'll be happy with both options. (Or make it a preference?).
[[x1 := nil] ensure: [x1 := (2 / 0)]] ensure: [self inform: x1 asString].
Hmm, this example of your nicely justifies your approach: Abandon = (ii) :D
Naming:
Hm, "light" reminds me of "friendly" and (iii) would certainly be more friendly (giving the process more freedom for cleaning things up). Difficult ... What about #terminateNow for (ii) and #terminate for (iii), slightly related to #updateStyle and #updateStyleNow? Since (iii) might continue with the execution of the current halfway-executed context, (ii) is more urgent. Another option would be "terminate: aggressive", analogously to "cleanup: aggressive" on Behavior where #terminate would fall back to "terminate: false". But this would make the new mode a little bit worse accessible.
Agreed, "light" is not good. Checked Visual Works again and they use terminate for (iii), terminateUnsafely for (ii) and terminateUnsafelyNow for kill (i). #teminateNow suggests it would terminate now as opposed to later :) No hint about the nature of the procedure that will attempt to terminate with increased level of possible damage to the environment... But we're getting closer and again, I'll be happy with either of them :)
Here are some ideas for better names:
(i) #destroy (as opposed to #kill, this sounds more like the cheap and unreactable operation it is) (ii) #terminateAggressively, which redirects to #terminate: true (iii) #terminate, which redirects to #terminate: false
One more naming suggestion: (iii) terminate (~run all unwind blocks) (ii) terminateLessSafely (~skip unwind blocks in progress, run all not-yet-started ones) (i) terminateUnsafely (~kill, run no unwinds)
Marcel wrote:
How would you summarize the differences between #terminate, #terminateAggressively, and #kill, each with 1-2 sentences? :-)
Inspired by VW:
(iii) "Terminate the process and allow all unwind blocks to run, even if they are currently in progress." This is the behavior we expect when terminating a healthy process.
(ii) "Terminate the process and run all not-yet-started unwind blocks; if the process is in the middle of an unwind block, then that unwind block will not be completed, but subsequent unwind blocks will be run." This is the behavior we expect when we close the debugger in a situation when the unwind block may not be able to complete, either because of an error, or because it is hung. In most other circumstances, we would want to use #terminate, which allows unwind blocks in progress to complete.
(i) "Terminate the process without attempting to run any of its unwind blocks." This is the behavior we might need in very rare circumstances when modes (iii) and (ii) fail.
Changeset:
Please find the attached changeset in which I have changed the names accordingly and also have removed the kill/destroy operation from the UI as per request of Marcel. I aggree with his argument that destroy is a kind of private operation that affects the interna of the process and should be only used for troubleshooting, so we should not give this hack too much visibility.
Nice! I liked your Abandon button with options more but this is a start. Kill option: do you mean no access to the kill option from the Process browser either? Process browser is a very troubleshooting tool :)
And finally, If I may - I'd slightly prefer not adding another #terminate: to the Process class only to serve a single sender but rather inline it and address the two termination modes directly in the ProcessBrowser class:
ProcessBrowser class >> #terminateProcess: aProcess aggressively: aggressive
aProcess ifNil: [^ self]. self suspendedProcesses removeKey: aProcess ifAbsent: []. - aProcess terminate: aggressive. + aggressive ifFalse: [aProcess terminate] ifTrue: [aProcess terminateAggressively]
Concerning your #unwindTo: issue - not sure if this actually related to this thread? Actually I've lost the overview which terminate mode - (ii) or (iii) - you are referring to, maybe we should discuss this in a separate conversation. :-)
Yes, it's a bug and deserves its own thread.
=== Summary:
TLDR: Concerning our next steps - consider this a small to-do list:
- [ ] We want to implement three modi of process termination:
- [x] (i) is not yet implemented but should be easy to implement
- [x] (ii) is in my proposal from [2], maybe needs further refinement
- [ ] (iii) is pretty much what we have in Process >> #terminate in the current Trunk. Is there anything still missing yet?
- [ ] Decide on names for (ii) and (iii) - see above
- [ ] Decide on UI integration into debugger - see above
Add this one?
- [ ] Decide on UI integration into Process Browser
Regarding Process >> #terminate - the current Trunk version is not final; the final version is the Inbox's Kernel-jar.1414.
More or less done! :)
Thanks,
Jaromir
On 2021-08-30T12:51:21+02:00, christoph.thiede at student.hpi.uni-potsdam.de wrote:
Hi Marcel, hi all,
How would you summarize the differences between #terminate, #terminateAggressively, and #kill, each with 1-2 sentences? :-)
Let's keep using the numbers from above instead, as the names are only my imperfect proposals:
(i) run no unwind contexts (harshest possible way; currently only achievable by doing "suspendedContext privSender: nil" prior to terminating). You would need this very rarely, if at all, for troubleshooting. (ii) run not-yet started unwind contexts (this is what I proposed in fix-Process-terminate.1.cs [1]). This is the behavior we expect from the Abandon button in a debugger. I think? (iii) run all unwind contexts, including those that already have been started (this is the most friendly way that you implemented in #terminate recently). This is the behavior we expect when terminating a healthy process.
Here are some ideas for better names:
(i) #destroy (as opposed to #kill, this sounds more like the cheap and unreactable operation it is) (ii) #terminateAggressively, which redirects to #terminate: true (iii) #terminate, which redirects to #terminate: false
Please find the attached changeset in which I have changed the names accordingly and also have removed the kill/destroy operation from the UI as per request of Marcel. I aggree with his argument that destroy is a kind of private operation that affects the interna of the process and should be only used for troubleshooting, so we should not give this hack too much visibility.
Best, Christoph
Sent from Squeak Inbox Talk
On 2021-08-23T13:59:04+02:00, marcel.taeumel at hpi.de wrote:
Hi Christoph --
How would you summarize the differences between #terminate, #terminateAggressively, and #kill, each with 1-2 sentences? :-)
Best, Marcel Am 23.08.2021 13:18:40 schrieb christoph.thiede at student.hpi.uni-potsdam.de <christoph.thiede at student.hpi.uni-potsdam.de>: Hi all, hi Jaromir,
based on the to-do list from my previous message, I have created a changeset that adds Process >> #terminateAggressively (ii) and Process >> #kill (i) and integrates them into the process browser and the debugger. Most importantly, the following snippet will inform "nil" now instead of "ZeroDevision" after abandoning the error:
????[[x1 := nil] ensure: [x1 := (2 / 0)]] ????????ensure: [self inform: x1 asString].
With this changeset, I would be fully happy again with the functionality of the debugger. :-) What remains to do is the following:
- [ ] We want to implement three modi of process termination:
- [x] (i) - see #kill
- [X] (ii) - see #terminateAggressively
- [ ] (iii) is pretty much what we have in Process >> #terminate in the current Trunk. Is there anything still missing yet?
- [ ] Decide on names for (ii) and (iii) - see my previous message
- [ ] Decide on UI integration into debugger - proposal exists in Process-terminateAggressively.1, but open for criticism
- [ ] Test #terminateAggressively (actualy, I don't have a good of overview of all your new #terminate tests - probably it would be easiest to wait until they have arrived in the Trunk and then create copies of them to test #terminateAggressively analogously :D)
Best, Christoph
Sent from Squeak Inbox Talk [https://github.com/hpi-swa-lab/squeak-inbox-talk]
On 2021-08-22T16:07:54+02:00, christoph.thiede at student.hpi.uni-potsdam.de wrote:
Hi Jaromir,
back from vacation, *finally* I have found some time to return back to this pleasant problem! :-)
Great, it sounds like we're already agreeing pretty much, just let me address some small points:
Debugger - Abandon could be the lightweight version you proposed. Why not have a proper Abandon button for it? ????The right click menu on a context could offer the Kill option (next to 'peel to first like this'); no button necessary. Now the question is what should be under the "window close" red-circle-x - heavyweight terminate? I'm thinking this scenario: if the debugger returns after closing the window you start thinking what happened and use Abandon; if it still doesn't help you go right-click and kill it?
My usual scenario is (limited experience however): look at something in the debugger (on a healthy process) and close the window (i.e. full termination is appropriate and I'd even say preferable). If something goes wrong - then I'd welcome a hint there are options - thus the proper Abandon button - what do you think?
Hm, I would rather say that closing the window should have the same effect as pressing "abandon" - personally, I am very much used to this and we do not really have good options to communicate a different behavior of the close button. I think presenting the new behaviors Kill (i) and (iii) in some kind of menu would be less confusing. A kill item in the stack trace menu sounds fine for now (even though killing is not actually related to the selected context). In the long term, I would like to introduce menu buttons in the fashion of [1] in the Morphic UI (Marcel is currently reviewing my proposal for this), then we could move Kill into the Abandon menu, but for now, your proposal sounds perfectly fine to me. :-)
look at something in the debugger (on a healthy process) and close the window (i.e. full termination is appropriate and I'd even say preferable).
Can you help me please, if the process is healthy (so no unwind context is lying on the stack), would there be any difference between the different terminate modi (ii) and (iii)?
But I don't have any good idea for version (ii) yet. Call it #abandon like in the debugger? Then again, #abandon is rather a verb from the Morphic language. Further possible vocables (according to my synonym thesaurus) include #end, #stop, #finish, #unwind, #abort, #exit. Please help... :-)
I'd probably go with something like #terminateLight because it's a proper process termination including unwinds except the ones currently in progress
- so it is a light version of #terminate :) I've checked VisualWorks: they
chose #terminateUnsafely for this type of termination which I don't like much, it sounds too negative; the real meaning is rather #terminateAsSafelyAsPossibleGivenTheCircumstances ;).
Hm, "light" reminds me of "friendly" and (iii) would certainly be more friendly (giving the process more freedom for cleaning things up). Difficult ... What about #terminateNow for (ii) and #terminate for (iii), slightly related to #updateStyle and #updateStyleNow? Since (iii) might continue with the execution of the current halfway-executed context, (ii) is more urgent. Another option would be "terminate: aggressive", analogously to "cleanup: aggressive" on Behavior where #terminate would fall back to "terminate: false". But this would make the new mode a little bit worse accessible.
Concerning your #unwindTo: issue - not sure if this actually related to this thread? Actually I've lost the overview which terminate mode - (ii) or (iii) - you are referring to, maybe we should discuss this in a separate conversation. :-)
TLDR: Concerning our next steps - consider this a small to-do list:
- [ ] We want to implement three modi of process termination:
????- [ ] (i) is not yet implemented but should be easy to implement ????- [ ] (ii) is in my proposal from [2], maybe needs further refinement ????- [ ] (iii) is pretty much what we have in Process >> #terminate in the current Trunk. Is there anything still missing yet?
- [ ] Decide on names for (ii) and (iii) - see above
- [ ] Decide on UI integration into debugger - see above
Further answers following soon.
Best, Christoph
[1] https://user-images.githubusercontent.com/48567372/60924070-4472fe80-a26e-11... [2] http://lists.squeakfoundation.org/pipermail/squeak-dev/2021-May/215608.html (fix-Process-terminate.cs)
Sent from Squeak Inbox Talk
On 2021-05-29T14:31:13-05:00, m at jaromir.net wrote:
Hi Christoph,
Jaromir, your proposal to provide multiple selectors for modeling separate modes of termination sounds like a very good idea to me. But how many different modes do we actually need? So far I can count three modes:
(i) run no unwind contexts (harshest possible way; currently only achievable by doing "suspendedContext privSender: nil" prior to terminating) (ii) run not-yet started unwind contexts (this is what I proposed in fix-Process-terminate.1.cs [1]) (iii) run all unwind contexts, including those that already have been started (this is the most friendly way that you implemented in #terminate recently)
I think this is it.
Litereally minutes ago had to use privSender: nil to get rid of a debugger :) Fully terminate really is too strong to recover from fatal errors.
... my point here is: Proceeding from an error almost always doesn't seem "right". :-) It is always a decision by the debugging programmer to override the default control flow and switch to the "next plausible alternative control flow", i.e., resume as if the error would have never been raised.
yes - I'd add: even an error may quite often be completely benign, like 'Transcript show: 1/0' - possibly a typo so you just may want to Proceed or fully terminate. In case the error damages a whole subsequent chain of events, you're absolutely right a full termination seems a silly option and a light version of terminate may be the most appropriate.
So I fully agree the decision which termination mode it is stays with the user - so I'm all for giving the user the choices you suggested.
\1. Which mode should we use in which situations?
I think this debate could benefit from a few more concrete usage scenarios. I'm just collecting some here (thinking aloud):
- Process Browser: We can provide multiple options in the process menu. - Debugger: I agree with you that Abandon should always run not-yet started unwind contexts but never resume halfway-executed unwind contexts. So this maps to to mode (ii) from above. - Skimming through most senders of #terminate in the image, they often orchestrate helper processes, deal with unhandled errors or timeouts, or do similar stuff - usually they should be very fine with the friendly version of #terminate, i.e. mode (iii) from above. I think. - Regarding option (1), I think you would need it extremely seldom but maybe in situations like when your stack contains a loop, your unwind contexts will cause a recursion/new error, or you deliberately want to prevent any unwind context from running. No objections against adding a small but decent button for this in the debugger. :-)
Would you agree with these behaviors? Maybe you can add further examples to the list?
Yes
Process Browser - the right click menu could provide all options
Debugger - Abandon could be the lightweight version you proposed. Why not have a proper Abandon button for it? ????The right click menu on a context could offer the Kill option (next to 'peel to first like this'); no button necessary. ????Now the question is what should be under the "window close" red-circle-x - heavyweight terminate? I'm thinking this scenario: if the debugger returns after closing the window you start thinking what happened and use Abandon; if it still doesn't help you go right-click and kill it?
My usual scenario is (limited experience however): look at something in the debugger (on a healthy process) and close the window (i.e. full termination is appropriate and I'd even say preferable). If something goes wrong - then I'd welcome a hint there are options - thus the proper Abandon button - what do you think?
\2. How should we name them?
Direct proposal: (i) #kill and (iii) #terminate. After looking up the original behavior of #terminate in Squeak 5.3, I think it would be consistent to resume all halfway-executed unwind contexts in this method. So yes, I also withdraw my criticism about #testNestedUnwind. :-)
But I don't have any good idea for version (ii) yet. Call it #abandon like in the debugger? Then again, #abandon is rather a verb from the Morphic language. Further possible vocables (according to my synonym thesaurus) include #end, #stop, #finish, #unwind, #abort, #exit. Please help... :-)
I'd probably go with something like #terminateLight because it's a proper process termination including unwinds except the ones currently in progress
- so it is a light version of #terminate :) I've checked VisualWorks: they
chose #terminateUnsafely for this type of termination which I don't like much, it sounds too negative; the real meaning is rather #terminateAsSafelyAsPossibleGivenTheCircumstances ;).
I'm wondering whether #unwindTo: (used ony by Generator) is bugged (with regard to dealing with non-local returns), and could be fixed/unified with your approach. Look at these examples:
p := [[Processor activeProcess suspend] valueUninterruptably] fork. Processor yield. p suspendedContext unwindTo: nil
or
p := [[:exit | [Processor activeProcess suspend] ensure: [exit value]] valueWithExit] fork. Processor yield. p suspendedContext unwindTo: nil
If you do `p terminate` instead of `p suspendedContext unwindTo: nil`, it works fine, but #unwindTo causes a block cannot return error - I think it's the same bug all over again :) #value evaluates the non-local return on the wrong stack...
Regarding our cannot return discussion - I have to think about it and I'll post my reply later in [1] to keep it separate :)
Thanks again and regards,
[1] http://forum.world.st/The-Inbox-Kernel-ct-1405-mcz-td5129706.html#a5130114
^[^ Jaromir
Sent from: http://forum.world.st/Squeak-Dev-f45488.html
Hi Christoph,
On 2021-12-16T03:10:36+01:00, christoph.thiede@student.hpi.uni-potsdam.de wrote:
I just have uploaded a revised version of Process-terminateAggressively.2.cs to the Trunk! :-) It includes #terminateAggressive & #destroy, the connection to the debugger/process browser, UI & E2E tests, and a bit of documentation. Please see the version message of Kernel-ct.1434 for more details.
Congratulations! Beautiful, in my opinion :) I like the classic "teminate" option in the Debugger's window menu and the new "destroy" option nicely hidden under the Shift + right-click menu ;)
Updated summary:
- [x] We want to implement three modi of process termination:
- [x] (i) - #destroy
- [x] (ii) - #terminateAggressively
- [ ] (iii) - #terminate, I still need to review your fixes
The latest is Kernel-jar.1436 (and KernelTests-jar.417)
- [x] Decide on names for (ii) and (iii) - done provided no objections
- [x] Decide on UI integration into debugger - done provided no objections
- [x] Decide on UI integration into Process Browser - done provided no objections
And just for some orientation, the other remaining issues:
- [ ] open #terminate issues (I still need to review them)
Yes
- [ ] proceedable BlockCannotReturn (I still need to review them?)
I'd say yes, I removed the dependency on your solution from the current #terminate; if agreed by others I'd be happy to see it implemented.
- [ ] Tackling Context>>#runUntilErrorReturnFrom: (most likely also my turn)
not sure what you're referring to
- [ ] issue with #unwindTo: - see http://lists.squeakfoundation.org/pipermail/squeak-dev/2021-May/215758.html#... (regression tests & patches welcome :))
yes, still pending; I just don't have enough energy now :)
Some by-the-way notes:
- I have merged some of your process tests earlier today. Thank you for providing them!
Many thanks indeed!
- I keep seeing some unexpected 'infinite recursion in doesNotUnderstand:' from time to time but could not reproduce them. Just FYI, afaik you and Eliot have concocted this - I still need to read up all the details in my overflowing inbox. ;-)
Purely Eliot's idea :) I haven't had any problem since the fix (but I'm mostly working with the new terminate in my image so maybe the current terminate has an issue - let me know if you find an example).
Thanks again for this nice addition to the Debugger.
Best, ~~~ ^[^ Jaromir
Sent from Squeak Inbox Talk
On 2021-11-15T18:33:33+01:00, mail at jaromir.net wrote:
Hi Christoph,
Once again, apologies for the huge delay in my responding :)
Hm, I would rather say that closing the window should have the same effect as pressing "abandon" - personally, I am very much used to this and we do not really have good options to communicate a different behavior of the close button.
Yes, you're right indeed.
A kill item in the stack trace menu sounds fine for now (even though killing is not actually related to the selected context). In the long term, I would like to introduce menu buttons in the fashion of [1] in the Morphic UI.
Your Abandon button with options is terrific, please disregard my suggestion :)
look at something in the debugger (on a healthy process) and close the window (i.e. full termination is appropriate and I'd even say preferable).
Can you help me please, if the process is healthy (so no unwind context is lying on the stack), would there be any difference between the different terminate modi (ii) and (iii)?
I thought there was a difference, e.g. if you debug #ensure on a healthy process and want to abandon the debugger in the middle - does it make sense?
Example: [^2] ensure: [Transcript cr; show: 'done'. "do some cleaning"]
Now, you start a debugger, do a few steps and in the middle of the ensure block you decide you've seen enough and hit Abandon - if it's implemented with (ii), i.e. the 'aggressive', less safe version of terminate, the block won't get completed. If it contained some cleaning procedures, the cleaning wouldn't be executed... That's why I'm still leaning slightly towards full termination as a default Abandon action - but e.g. Visual Works use the less safe termination in this case, as you suggest. Other Smalltalks I checked (Cuis, Pharo and VA) don't have three ways to terminate at all (yet) :) However, ultimately users that use Squeak the most should have the final say. I'll be happy with both options. (Or make it a preference?).
[[x1 := nil] ensure: [x1 := (2 / 0)]] ����ensure: [self inform: x1 asString].
Hmm, this example of your nicely justifies your approach: Abandon = (ii) :D
Naming:
Hm, "light" reminds me of "friendly" and (iii) would certainly be more friendly (giving the process more freedom for cleaning things up). Difficult ... What about #terminateNow for (ii) and #terminate for (iii), slightly related to #updateStyle and #updateStyleNow? Since (iii) might continue with the execution of the current halfway-executed context, (ii) is more urgent. Another option would be "terminate: aggressive", analogously to "cleanup: aggressive" on Behavior where #terminate would fall back to "terminate: false". But this would make the new mode a little bit worse accessible.
Agreed, "light" is not good. Checked Visual Works again and they use terminate for (iii), terminateUnsafely for (ii) and terminateUnsafelyNow for kill (i). #teminateNow suggests it would terminate now as opposed to later :) No hint about the nature of the procedure that will attempt to terminate with increased level of possible damage to the environment... But we're getting closer and again, I'll be happy with either of them :)
Here are some ideas for better names:
(i) #destroy (as opposed to #kill, this sounds more like the cheap and unreactable operation it is) (ii) #terminateAggressively, which redirects to #terminate: true (iii) #terminate, which redirects to #terminate: false
One more naming suggestion: (iii) terminate (~run all unwind blocks) (ii) terminateLessSafely (~skip unwind blocks in progress, run all not-yet-started ones) (i) terminateUnsafely (~kill, run no unwinds)
Marcel wrote:
How would you summarize the differences between #terminate, #terminateAggressively, and #kill, each with 1-2 sentences? :-)
Inspired by VW:
(iii) "Terminate the process and allow all unwind blocks to run, even if they are currently in progress." This is the behavior we expect when terminating a healthy process.
(ii) "Terminate the process and run all not-yet-started unwind blocks; if the process is in the middle of an unwind block, then that unwind block will not be completed, but subsequent unwind blocks will be run." This is the behavior we expect when we close the debugger in a situation when the unwind block may not be able to complete, either because of an error, or because it is hung. In most other circumstances, we would want to use #terminate, which allows unwind blocks in progress to complete.
(i) "Terminate the process without attempting to run any of its unwind blocks." This is the behavior we might need in very rare circumstances when modes (iii) and (ii) fail.
Changeset:
Please find the attached changeset in which I have changed the names accordingly and also have removed the kill/destroy operation from the UI as per request of Marcel. I aggree with his argument that destroy is a kind of private operation that affects the interna of the process and should be only used for troubleshooting, so we should not give this hack too much visibility.
Nice! I liked your Abandon button with options more but this is a start. Kill option: do you mean no access to the kill option from the Process browser either? Process browser is a very troubleshooting tool :)
And finally, If I may - I'd slightly prefer not adding another #terminate: to the Process class only to serve a single sender but rather inline it and address the two termination modes directly in the ProcessBrowser class:
ProcessBrowser class >> #terminateProcess: aProcess aggressively: aggressive
����aProcess ifNil: [^ self]. ���� ����self suspendedProcesses ��������removeKey: aProcess ��������ifAbsent: []. -����aProcess terminate: aggressive. +����aggressive ��������ifFalse: [aProcess terminate] ��������ifTrue: [aProcess terminateAggressively]
Concerning your #unwindTo: issue - not sure if this actually related to this thread? Actually I've lost the overview which terminate mode - (ii) or (iii) - you are referring to, maybe we should discuss this in a separate conversation. :-)
Yes, it's a bug and deserves its own thread.
=== Summary:
TLDR: Concerning our next steps - consider this a small to-do list:
- [ ] We want to implement three modi of process termination:
- [x] (i) is not yet implemented but should be easy to implement
- [x] (ii) is in my proposal from [2], maybe needs further refinement
- [ ] (iii) is pretty much what we have in Process >> #terminate in the current Trunk. Is there anything still missing yet?
- [ ] Decide on names for (ii) and (iii) - see above
- [ ] Decide on UI integration into debugger - see above
Add this one?
- [ ] Decide on UI integration into Process Browser
Regarding Process >> #terminate - the current Trunk version is not final; the final version is the Inbox's Kernel-jar.1414.
More or less done! :)
Thanks,
Jaromir
On 2021-08-30T12:51:21+02:00, christoph.thiede at student.hpi.uni-potsdam.de wrote:
Hi Marcel, hi all,
How would you summarize the differences between #terminate, #terminateAggressively, and #kill, each with 1-2 sentences? :-)
Let's keep using the numbers from above instead, as the names are only my imperfect proposals:
(i) run no unwind contexts (harshest possible way; currently only achievable by doing "suspendedContext privSender: nil" prior to terminating). You would need this very rarely, if at all, for troubleshooting. (ii) run not-yet started unwind contexts (this is what I proposed in fix-Process-terminate.1.cs [1]). This is the behavior we expect from the Abandon button in a debugger. I think? (iii) run all unwind contexts, including those that already have been started (this is the most friendly way that you implemented in #terminate recently). This is the behavior we expect when terminating a healthy process.
Here are some ideas for better names:
(i) #destroy (as opposed to #kill, this sounds more like the cheap and unreactable operation it is) (ii) #terminateAggressively, which redirects to #terminate: true (iii) #terminate, which redirects to #terminate: false
Please find the attached changeset in which I have changed the names accordingly and also have removed the kill/destroy operation from the UI as per request of Marcel. I aggree with his argument that destroy is a kind of private operation that affects the interna of the process and should be only used for troubleshooting, so we should not give this hack too much visibility.
Best, Christoph
Sent from Squeak Inbox Talk
On 2021-08-23T13:59:04+02:00, marcel.taeumel at hpi.de wrote:
Hi Christoph --
How would you summarize the differences between #terminate, #terminateAggressively, and #kill, each with 1-2 sentences? :-)
Best, Marcel Am 23.08.2021 13:18:40 schrieb christoph.thiede at student.hpi.uni-potsdam.de <christoph.thiede at student.hpi.uni-potsdam.de>: Hi all, hi Jaromir,
based on the to-do list from my previous message, I have created a changeset that adds Process >> #terminateAggressively (ii) and Process >> #kill (i) and integrates them into the process browser and the debugger. Most importantly, the following snippet will inform "nil" now instead of "ZeroDevision" after abandoning the error:
????[[x1 := nil] ensure: [x1 := (2 / 0)]] ????????ensure: [self inform: x1 asString].
With this changeset, I would be fully happy again with the functionality of the debugger. :-) What remains to do is the following:
- [ ] We want to implement three modi of process termination:
- [x] (i) - see #kill
- [X] (ii) - see #terminateAggressively
- [ ] (iii) is pretty much what we have in Process >> #terminate in the current Trunk. Is there anything still missing yet?
- [ ] Decide on names for (ii) and (iii) - see my previous message
- [ ] Decide on UI integration into debugger - proposal exists in Process-terminateAggressively.1, but open for criticism
- [ ] Test #terminateAggressively (actualy, I don't have a good of overview of all your new #terminate tests - probably it would be easiest to wait until they have arrived in the Trunk and then create copies of them to test #terminateAggressively analogously :D)
Best, Christoph
Sent from Squeak Inbox Talk [https://github.com/hpi-swa-lab/squeak-inbox-talk]
On 2021-08-22T16:07:54+02:00, christoph.thiede at student.hpi.uni-potsdam.de wrote:
Hi Jaromir,
back from vacation, *finally* I have found some time to return back to this pleasant problem! :-)
Great, it sounds like we're already agreeing pretty much, just let me address some small points:
Debugger - Abandon could be the lightweight version you proposed. Why not have a proper Abandon button for it? ????The right click menu on a context could offer the Kill option (next to 'peel to first like this'); no button necessary. Now the question is what should be under the "window close" red-circle-x - heavyweight terminate? I'm thinking this scenario: if the debugger returns after closing the window you start thinking what happened and use Abandon; if it still doesn't help you go right-click and kill it?
My usual scenario is (limited experience however): look at something in the debugger (on a healthy process) and close the window (i.e. full termination is appropriate and I'd even say preferable). If something goes wrong - then I'd welcome a hint there are options - thus the proper Abandon button - what do you think?
Hm, I would rather say that closing the window should have the same effect as pressing "abandon" - personally, I am very much used to this and we do not really have good options to communicate a different behavior of the close button. I think presenting the new behaviors Kill (i) and (iii) in some kind of menu would be less confusing. A kill item in the stack trace menu sounds fine for now (even though killing is not actually related to the selected context). In the long term, I would like to introduce menu buttons in the fashion of [1] in the Morphic UI (Marcel is currently reviewing my proposal for this), then we could move Kill into the Abandon menu, but for now, your proposal sounds perfectly fine to me. :-)
look at something in the debugger (on a healthy process) and close the window (i.e. full termination is appropriate and I'd even say preferable).
Can you help me please, if the process is healthy (so no unwind context is lying on the stack), would there be any difference between the different terminate modi (ii) and (iii)?
> But I don't have any good idea for version (ii) yet. Call it #abandon like > in the debugger? Then again, #abandon is rather a verb from the Morphic > language. Further possible vocables (according to my synonym thesaurus) > include #end, #stop, #finish, #unwind, #abort, #exit. Please help... :-)
I'd probably go with something like #terminateLight because it's a proper process termination including unwinds except the ones currently in progress
- so it is a light version of #terminate :) I've checked VisualWorks: they
chose #terminateUnsafely for this type of termination which I don't like much, it sounds too negative; the real meaning is rather #terminateAsSafelyAsPossibleGivenTheCircumstances ;).
Hm, "light" reminds me of "friendly" and (iii) would certainly be more friendly (giving the process more freedom for cleaning things up). Difficult ... What about #terminateNow for (ii) and #terminate for (iii), slightly related to #updateStyle and #updateStyleNow? Since (iii) might continue with the execution of the current halfway-executed context, (ii) is more urgent. Another option would be "terminate: aggressive", analogously to "cleanup: aggressive" on Behavior where #terminate would fall back to "terminate: false". But this would make the new mode a little bit worse accessible.
Concerning your #unwindTo: issue - not sure if this actually related to this thread? Actually I've lost the overview which terminate mode - (ii) or (iii) - you are referring to, maybe we should discuss this in a separate conversation. :-)
TLDR: Concerning our next steps - consider this a small to-do list:
- [ ] We want to implement three modi of process termination:
????- [ ] (i) is not yet implemented but should be easy to implement ????- [ ] (ii) is in my proposal from [2], maybe needs further refinement ????- [ ] (iii) is pretty much what we have in Process >> #terminate in the current Trunk. Is there anything still missing yet?
- [ ] Decide on names for (ii) and (iii) - see above
- [ ] Decide on UI integration into debugger - see above
Further answers following soon.
Best, Christoph
[1] https://user-images.githubusercontent.com/48567372/60924070-4472fe80-a26e-11... [2] http://lists.squeakfoundation.org/pipermail/squeak-dev/2021-May/215608.html (fix-Process-terminate.cs)
Sent from Squeak Inbox Talk
On 2021-05-29T14:31:13-05:00, m at jaromir.net wrote:
Hi Christoph,
> Jaromir, your proposal to provide multiple selectors for modeling separate > modes of termination sounds like a very good idea to me. But how many > different modes do we actually need? So far I can count three modes: > > (i) run no unwind contexts (harshest possible way; currently only > achievable by doing "suspendedContext privSender: nil" prior to > terminating) > (ii) run not-yet started unwind contexts (this is what I proposed in > fix-Process-terminate.1.cs [1]) > (iii) run all unwind contexts, including those that already have been > started (this is the most friendly way that you implemented in #terminate > recently)
I think this is it.
Litereally minutes ago had to use privSender: nil to get rid of a debugger :) Fully terminate really is too strong to recover from fatal errors.
> ... my point here is: Proceeding from an error almost always doesn't seem > "right". :-) It is always a decision by the debugging programmer to > override the default control flow and switch to the "next plausible > alternative control flow", i.e., resume as if the error would have never > been raised.
yes - I'd add: even an error may quite often be completely benign, like 'Transcript show: 1/0' - possibly a typo so you just may want to Proceed or fully terminate. In case the error damages a whole subsequent chain of events, you're absolutely right a full termination seems a silly option and a light version of terminate may be the most appropriate.
So I fully agree the decision which termination mode it is stays with the user - so I'm all for giving the user the choices you suggested.
> \1. Which mode should we use in which situations? > > I think this debate could benefit from a few more concrete usage > scenarios. I'm just collecting some here (thinking aloud): > > - Process Browser: We can provide multiple options in the process menu. > - Debugger: I agree with you that Abandon should always run not-yet > started unwind contexts but never resume halfway-executed unwind contexts. > So this maps to to mode (ii) from above. > - Skimming through most senders of #terminate in the image, they often > orchestrate helper processes, deal with unhandled errors or timeouts, or > do similar stuff - usually they should be very fine with the friendly > version of #terminate, i.e. mode (iii) from above. I think. > - Regarding option (1), I think you would need it extremely seldom but > maybe in situations like when your stack contains a loop, your unwind > contexts will cause a recursion/new error, or you deliberately want to > prevent any unwind context from running. No objections against adding a > small but decent button for this in the debugger. :-) > > Would you agree with these behaviors? Maybe you can add further examples > to the list?
Yes
Process Browser - the right click menu could provide all options
Debugger - Abandon could be the lightweight version you proposed. Why not have a proper Abandon button for it? ????The right click menu on a context could offer the Kill option (next to 'peel to first like this'); no button necessary. ????Now the question is what should be under the "window close" red-circle-x - heavyweight terminate? I'm thinking this scenario: if the debugger returns after closing the window you start thinking what happened and use Abandon; if it still doesn't help you go right-click and kill it?
My usual scenario is (limited experience however): look at something in the debugger (on a healthy process) and close the window (i.e. full termination is appropriate and I'd even say preferable). If something goes wrong - then I'd welcome a hint there are options - thus the proper Abandon button - what do you think?
> \2. How should we name them? > > Direct proposal: (i) #kill and (iii) #terminate. > After looking up the original behavior of #terminate in Squeak 5.3, I > think it would be consistent to resume all halfway-executed unwind > contexts in this method. So yes, I also withdraw my criticism about > #testNestedUnwind. :-) > > But I don't have any good idea for version (ii) yet. Call it #abandon like > in the debugger? Then again, #abandon is rather a verb from the Morphic > language. Further possible vocables (according to my synonym thesaurus) > include #end, #stop, #finish, #unwind, #abort, #exit. Please help... :-)
I'd probably go with something like #terminateLight because it's a proper process termination including unwinds except the ones currently in progress
- so it is a light version of #terminate :) I've checked VisualWorks: they
chose #terminateUnsafely for this type of termination which I don't like much, it sounds too negative; the real meaning is rather #terminateAsSafelyAsPossibleGivenTheCircumstances ;).
I'm wondering whether #unwindTo: (used ony by Generator) is bugged (with regard to dealing with non-local returns), and could be fixed/unified with your approach. Look at these examples:
p := [[Processor activeProcess suspend] valueUninterruptably] fork. Processor yield. p suspendedContext unwindTo: nil
or
p := [[:exit | [Processor activeProcess suspend] ensure: [exit value]] valueWithExit] fork. Processor yield. p suspendedContext unwindTo: nil
If you do `p terminate` instead of `p suspendedContext unwindTo: nil`, it works fine, but #unwindTo causes a block cannot return error - I think it's the same bug all over again :) #value evaluates the non-local return on the wrong stack...
Regarding our cannot return discussion - I have to think about it and I'll post my reply later in [1] to keep it separate :)
Thanks again and regards,
[1] http://forum.world.st/The-Inbox-Kernel-ct-1405-mcz-td5129706.html#a5130114
^[^ Jaromir
Sent from: http://forum.world.st/Squeak-Dev-f45488.html
Hi Jaromir,
I'm responding to this message because you introduce useful categorisation of different kinds of terminate. I'll respond in context below.
On Mon, Nov 15, 2021 at 9:41 AM mail@jaromir.net wrote:
Hi Christoph,
Once again, apologies for the huge delay in my responding :)
Hm, I would rather say that closing the window should have the same
effect as pressing "abandon" - personally, I am very much used to this and we do not really have good options to communicate a different behavior of the close button.
Yes, you're right indeed.
A kill item in the stack trace menu sounds fine for now (even though
killing is not actually related to the selected context). In the long term, I would like to introduce menu buttons in the fashion of [1] in the Morphic UI.
Your Abandon button with options is terrific, please disregard my suggestion :)
look at something in the debugger (on a healthy process) and close the
window (i.e. full termination is appropriate and I'd even say preferable).
Can you help me please, if the process is healthy (so no unwind context
is lying on the stack), would there be any difference between the different terminate modi (ii) and (iii)?
I thought there was a difference, e.g. if you debug #ensure on a healthy process and want to abandon the debugger in the middle - does it make sense?
Example: [^2] ensure: [Transcript cr; show: 'done'. "do some cleaning"]
Now, you start a debugger, do a few steps and in the middle of the ensure block you decide you've seen enough and hit Abandon - if it's implemented with (ii), i.e. the 'aggressive', less safe version of terminate, the block won't get completed. If it contained some cleaning procedures, the cleaning wouldn't be executed... That's why I'm still leaning slightly towards full termination as a default Abandon action - but e.g. Visual Works use the less safe termination in this case, as you suggest. Other Smalltalks I checked (Cuis, Pharo and VA) don't have three ways to terminate at all (yet) :) However, ultimately users that use Squeak the most should have the final say. I'll be happy with both options. (Or make it a preference?).
[[x1 := nil] ensure: [x1 := (2 / 0)]] ensure: [self inform: x1 asString].
Hmm, this example of your nicely justifies your approach: Abandon = (ii) :D
Naming:
Hm, "light" reminds me of "friendly" and (iii) would certainly be more
friendly (giving the process more freedom for cleaning things up). Difficult ...
What about #terminateNow for (ii) and #terminate for (iii), slightly
related to #updateStyle and #updateStyleNow? Since (iii) might continue with the execution of the current halfway-executed context, (ii) is more urgent.
Another option would be "terminate: aggressive", analogously to
"cleanup: aggressive" on Behavior where #terminate would fall back to "terminate: false". But this would make the new mode a little bit worse accessible.
Agreed, "light" is not good. Checked Visual Works again and they use terminate for (iii), terminateUnsafely for (ii) and terminateUnsafelyNow for kill (i). #teminateNow suggests it would terminate now as opposed to later :) No hint about the nature of the procedure that will attempt to terminate with increased level of possible damage to the environment... But we're getting closer and again, I'll be happy with either of them :)
Here are some ideas for better names:
(i) #destroy (as opposed to #kill, this sounds more like the cheap and
unreactable operation it is)
(ii) #terminateAggressively, which redirects to #terminate: true (iii) #terminate, which redirects to #terminate: false
One more naming suggestion: (iii) terminate (~run all unwind blocks) (ii) terminateLessSafely (~skip unwind blocks in progress, run all not-yet-started ones) (i) terminateUnsafely (~kill, run no unwinds)
OK, this is really useful. It provides the straight-forward options we have for process termination. Now let's try and be categorical about unwind blocks.
Unwind blocks are used to maintain invariants. The classic example is Semaphore>>critical:
critical: mutuallyExcludedBlock "Evaluate mutuallyExcludedBlock only if the receiver is not currently in the process of running the critical: message. If the receiver is, evaluate mutuallyExcludedBlock after the other critical: message is finished." <criticalSection> self wait. ^mutuallyExcludedBlock ensure: [self signal]
Given that unwind blocks are used to maintain invariants, any terminate which does (iii), run all unwind blocks, including those that have already been run, is completely broken. We can therefore ignore (i). It would run unwinds more than once. The correct terminate is (ii). Skip unwind blocks already in progress. Presumably something very wrong has happened in an unwind block that has not completed. But all unwinds further away from top of stack should and must be run normally. Note that in the normal case there will be no such partial unwinds; it will not occur in correct code. Note further that its extremely unlikely that there will be an unwind within an unwind.
But there's an assumption here. In normal termination, unwind blocks will just run, and the process will terminate cleanly. If it terminates itself then there is no issue other than potential bugs in unwinds (likely during development, rather than normal running). An application that wants to terminate other processes should be written to terminate those processes it needs to only when those processes are at some safe point. I would not spend effort trying to make terminate safe when sent from other processes at arbitrary points. That counts as trying to shoot oneself in the foot.
So terminate boils down to two or three cases, depending on how you look at it. The first case, or pair of cases, is a process that terminates itself or is terminated by another process while at a safe point. In this case any and all unwinds *must* be run. The final case is a process that is stuck in termination, presumably due to a bug in an unwind. This is the only point where we have the choice between ii and i.
So IMO the choice should be between terminate (ii) and terminateUnsafely (i). [I believe that in VisualWorks I called (i) terminateWithExtremePrejudice, after Apocalypse Now]. When a process starts to terminate, it is marked somehow as being terminated, running unwind blocks. If the normal terminate is sent to the process while in this state that terminate send should raise an error; attempting to terminate a process twice is an error. We cannot automatically decide if a process in this state is correctly running unwinds; that would be equivalent to solving the halting problem. Therefore we leave it up to the programmer to decide if, on discovering a terminating process stuck somewhere in running unwinds, they terminateUnsafely.
A key attitude here is to make the common case work well. Don't let the perfect be the enemy of the good. In this case there is no perfect solution; arbitrary errors *can* occur, *potentially*, in termination, but in practice are extremely rare. Keeping the system simple, comprehensible and maintainable is a prime directive. If a programmer can, for example, use the debugger to manually run undins that have not yet run because of a stuck unwind, then there is no absolute need for the system to handle this situation gracefully. A developer that gets themselves in hot water creating errors iu unwinds can get themselves out using the debugger.
So for me, KISS. Just support something that guards against multiple termination, and in the debugger, reveals clearly where a process is in the course of running unwinds.
And I really *don'* like the idea of running terminate in a separate process. This has bad performance implications. Most terminations can be done in the process itself; that should be the expectation.
Marcel wrote:
How would you summarize the differences between #terminate,
#terminateAggressively, and #kill, each with 1-2 sentences? :-)
Inspired by VW:
(iii) "Terminate the process and allow all unwind blocks to run, even if they are currently in progress." This is the behavior we expect when terminating a healthy process.
(ii) "Terminate the process and run all not-yet-started unwind blocks; if the process is in the middle of an unwind block, then that unwind block will not be completed, but subsequent unwind blocks will be run." This is the behavior we expect when we close the debugger in a situation when the unwind block may not be able to complete, either because of an error, or because it is hung. In most other circumstances, we would want to use #terminate, which allows unwind blocks in progress to complete.
(i) "Terminate the process without attempting to run any of its unwind blocks." This is the behavior we might need in very rare circumstances when modes (iii) and (ii) fail.
Changeset:
Please find the attached changeset in which I have changed the names
accordingly and also have removed the kill/destroy operation from the UI as per request of Marcel. I aggree with his argument that destroy is a kind of private operation that affects the interna of the process and should be only used for troubleshooting, so we should not give this hack too much visibility.
Nice! I liked your Abandon button with options more but this is a start. Kill option: do you mean no access to the kill option from the Process browser either? Process browser is a very troubleshooting tool :)
And finally, If I may - I'd slightly prefer not adding another #terminate: to the Process class only to serve a single sender but rather inline it and address the two termination modes directly in the ProcessBrowser class:
ProcessBrowser class >> #terminateProcess: aProcess aggressively: aggressive
aProcess ifNil: [^ self]. self suspendedProcesses removeKey: aProcess ifAbsent: [].
aProcess terminate: aggressive.
aggressive ifFalse: [aProcess terminate] ifTrue: [aProcess terminateAggressively]
Concerning your #unwindTo: issue - not sure if this actually related to
this thread? Actually I've lost the overview which terminate mode - (ii) or (iii) - you are referring to, maybe we should discuss this in a separate conversation. :-)
Yes, it's a bug and deserves its own thread.
=== Summary:
TLDR: Concerning our next steps - consider this a small to-do list:
- [ ] We want to implement three modi of process termination:
- [x] (i) is not yet implemented but should be easy to implement
- [x] (ii) is in my proposal from [2], maybe needs further refinement
- [ ] (iii) is pretty much what we have in Process >> #terminate in
the current Trunk. Is there anything still missing yet?
- [ ] Decide on names for (ii) and (iii) - see above
- [ ] Decide on UI integration into debugger - see above
Add this one?
- [ ] Decide on UI integration into Process Browser
Regarding Process >> #terminate - the current Trunk version is not final; the final version is the Inbox's Kernel-jar.1414.
More or less done! :)
Thanks,
Jaromir
On 2021-08-30T12:51:21+02:00, christoph.thiede@student.hpi.uni-potsdam.de wrote:
Hi Marcel, hi all,
How would you summarize the differences between #terminate,
#terminateAggressively, and #kill, each with 1-2 sentences? :-)
Let's keep using the numbers from above instead, as the names are only
my imperfect proposals:
(i) run no unwind contexts (harshest possible way; currently only
achievable by doing "suspendedContext privSender: nil" prior to terminating). You would need this very rarely, if at all, for troubleshooting.
(ii) run not-yet started unwind contexts (this is what I proposed in
fix-Process-terminate.1.cs [1]). This is the behavior we expect from the Abandon button in a debugger. I think?
(iii) run all unwind contexts, including those that already have been
started (this is the most friendly way that you implemented in #terminate recently). This is the behavior we expect when terminating a healthy process.
Here are some ideas for better names:
(i) #destroy (as opposed to #kill, this sounds more like the cheap and
unreactable operation it is)
(ii) #terminateAggressively, which redirects to #terminate: true (iii) #terminate, which redirects to #terminate: false
Please find the attached changeset in which I have changed the names
accordingly and also have removed the kill/destroy operation from the UI as per request of Marcel. I aggree with his argument that destroy is a kind of private operation that affects the interna of the process and should be only used for troubleshooting, so we should not give this hack too much visibility.
Best, Christoph
Sent from Squeak Inbox Talk
On 2021-08-23T13:59:04+02:00, marcel.taeumel at hpi.de wrote:
Hi Christoph --
How would you summarize the differences between #terminate,
#terminateAggressively, and #kill, each with 1-2 sentences? :-)
Best, Marcel Am 23.08.2021 13:18:40 schrieb christoph.thiede at
student.hpi.uni-potsdam.de <christoph.thiede at student.hpi.uni-potsdam.de
:
Hi all, hi Jaromir,
based on the to-do list from my previous message, I have created a
changeset that adds Process >> #terminateAggressively (ii) and Process >> #kill (i) and integrates them into the process browser and the debugger. Most importantly, the following snippet will inform "nil" now instead of "ZeroDevision" after abandoning the error:
����[[x1 := nil] ensure: [x1 := (2 / 0)]] ��������ensure: [self inform: x1 asString].
With this changeset, I would be fully happy again with the
functionality of the debugger. :-) What remains to do is the following:
- [ ] We want to implement three modi of process termination:
- [x] (i) - see #kill
- [X] (ii) - see #terminateAggressively
- [ ] (iii) is pretty much what we have in Process >> #terminate in
the current Trunk. Is there anything still missing yet?
- [ ] Decide on names for (ii) and (iii) - see my previous message
- [ ] Decide on UI integration into debugger - proposal exists in
Process-terminateAggressively.1, but open for criticism
- [ ] Test #terminateAggressively (actualy, I don't have a good of
overview of all your new #terminate tests - probably it would be easiest to wait until they have arrived in the Trunk and then create copies of them to test #terminateAggressively analogously :D)
Best, Christoph
Sent from Squeak Inbox Talk [
https://github.com/hpi-swa-lab/squeak-inbox-talk]
On 2021-08-22T16:07:54+02:00, christoph.thiede at
student.hpi.uni-potsdam.de wrote:
Hi Jaromir,
back from vacation, *finally* I have found some time to return back
to this pleasant problem! :-)
Great, it sounds like we're already agreeing pretty much, just let
me address some small points:
Debugger - Abandon could be the lightweight version you proposed.
Why not
have a proper Abandon button for it? ����The right click menu on a context could offer the Kill option
(next to
'peel to first like this'); no button necessary. Now the question is what should be under the "window close"
red-circle-x -
heavyweight terminate? I'm thinking this scenario: if the debugger
returns
after closing the window you start thinking what happened and use
Abandon;
if it still doesn't help you go right-click and kill it?
My usual scenario is (limited experience however): look at
something in the
debugger (on a healthy process) and close the window (i.e. full
termination
is appropriate and I'd even say preferable). If something goes
wrong - then
I'd welcome a hint there are options - thus the proper Abandon
button - what
do you think?
Hm, I would rather say that closing the window should have the same
effect as pressing "abandon" - personally, I am very much used to this and we do not really have good options to communicate a different behavior of the close button. I think presenting the new behaviors Kill (i) and (iii) in some kind of menu would be less confusing.
A kill item in the stack trace menu sounds fine for now (even though
killing is not actually related to the selected context). In the long term, I would like to introduce menu buttons in the fashion of [1] in the Morphic UI (Marcel is currently reviewing my proposal for this), then we could move Kill into the Abandon menu, but for now, your proposal sounds perfectly fine to me. :-)
look at something in the debugger (on a healthy process) and close
the window (i.e. full termination is appropriate and I'd even say preferable).
Can you help me please, if the process is healthy (so no unwind
context is lying on the stack), would there be any difference between the different terminate modi (ii) and (iii)?
But I don't have any good idea for version (ii) yet. Call it
#abandon like
in the debugger? Then again, #abandon is rather a verb from the
Morphic
language. Further possible vocables (according to my synonym
thesaurus)
include #end, #stop, #finish, #unwind, #abort, #exit. Please
help... :-)
I'd probably go with something like #terminateLight because it's a
proper
process termination including unwinds except the ones currently in
progress
- so it is a light version of #terminate :) I've checked
VisualWorks: they
chose #terminateUnsafely for this type of termination which I
don't like
much, it sounds too negative; the real meaning is rather #terminateAsSafelyAsPossibleGivenTheCircumstances ;).
Hm, "light" reminds me of "friendly" and (iii) would certainly be
more friendly (giving the process more freedom for cleaning things up). Difficult ...
What about #terminateNow for (ii) and #terminate for (iii), slightly
related to #updateStyle and #updateStyleNow? Since (iii) might continue with the execution of the current halfway-executed context, (ii) is more urgent.
Another option would be "terminate: aggressive", analogously to
"cleanup: aggressive" on Behavior where #terminate would fall back to "terminate: false". But this would make the new mode a little bit worse accessible.
Concerning your #unwindTo: issue - not sure if this actually related
to this thread? Actually I've lost the overview which terminate mode - (ii) or (iii) - you are referring to, maybe we should discuss this in a separate conversation. :-)
TLDR: Concerning our next steps - consider this a small to-do list:
- [ ] We want to implement three modi of process termination:
����- [ ] (i) is not yet implemented but should be easy to implement ����- [ ] (ii) is in my proposal from [2], maybe needs further
refinement
����- [ ] (iii) is pretty much what we have in Process >> #terminate
in the current Trunk. Is there anything still missing yet?
- [ ] Decide on names for (ii) and (iii) - see above
- [ ] Decide on UI integration into debugger - see above
Further answers following soon.
Best, Christoph
[1]
https://user-images.githubusercontent.com/48567372/60924070-4472fe80-a26e-11...
[2]
http://lists.squeakfoundation.org/pipermail/squeak-dev/2021-May/215608.html (fix-Process-terminate.cs)
Sent from Squeak Inbox Talk
On 2021-05-29T14:31:13-05:00, m at jaromir.net wrote:
Hi Christoph,
Jaromir, your proposal to provide multiple selectors for
modeling separate
modes of termination sounds like a very good idea to me. But how
many
different modes do we actually need? So far I can count three
modes:
(i) run no unwind contexts (harshest possible way; currently only achievable by doing "suspendedContext privSender: nil" prior to terminating) (ii) run not-yet started unwind contexts (this is what I
proposed in
fix-Process-terminate.1.cs [1]) (iii) run all unwind contexts, including those that already have
been
started (this is the most friendly way that you implemented in
#terminate
recently)
I think this is it.
Litereally minutes ago had to use privSender: nil to get rid of a
debugger
:) Fully terminate really is too strong to recover from fatal
errors.
... my point here is: Proceeding from an error almost always
doesn't seem
"right". :-) It is always a decision by the debugging programmer
to
override the default control flow and switch to the "next
plausible
alternative control flow", i.e., resume as if the error would
have never
been raised.
yes - I'd add: even an error may quite often be completely benign,
like
'Transcript show: 1/0' - possibly a typo so you just may want to
Proceed or
fully terminate. In case the error damages a whole subsequent
chain of
events, you're absolutely right a full termination seems a silly
option and
a light version of terminate may be the most appropriate.
So I fully agree the decision which termination mode it is stays
with the
user - so I'm all for giving the user the choices you suggested.
\1. Which mode should we use in which situations?
I think this debate could benefit from a few more concrete usage scenarios. I'm just collecting some here (thinking aloud):
- Process Browser: We can provide multiple options in the
process menu.
- Debugger: I agree with you that Abandon should always run
not-yet
started unwind contexts but never resume halfway-executed unwind
contexts.
So this maps to to mode (ii) from above. - Skimming through most senders of #terminate in the image,
they often
orchestrate helper processes, deal with unhandled errors or
timeouts, or
do similar stuff - usually they should be very fine with the
friendly
version of #terminate, i.e. mode (iii) from above. I think. - Regarding option (1), I think you would need it extremely
seldom but
maybe in situations like when your stack contains a loop, your
unwind
contexts will cause a recursion/new error, or you deliberately
want to
prevent any unwind context from running. No objections against
adding a
small but decent button for this in the debugger. :-)
Would you agree with these behaviors? Maybe you can add further
examples
to the list?
Yes
Process Browser - the right click menu could provide all options
Debugger - Abandon could be the lightweight version you proposed.
Why not
have a proper Abandon button for it? ����The right click menu on a context could offer the Kill option
(next to
'peel to first like this'); no button necessary. ����Now the question is what should be under the "window close"
red-circle-x -
heavyweight terminate? I'm thinking this scenario: if the debugger
returns
after closing the window you start thinking what happened and use
Abandon;
if it still doesn't help you go right-click and kill it?
My usual scenario is (limited experience however): look at
something in the
debugger (on a healthy process) and close the window (i.e. full
termination
is appropriate and I'd even say preferable). If something goes
wrong - then
I'd welcome a hint there are options - thus the proper Abandon
button - what
do you think?
\2. How should we name them?
Direct proposal: (i) #kill and (iii) #terminate. After looking up the original behavior of #terminate in Squeak
5.3, I
think it would be consistent to resume all halfway-executed
unwind
contexts in this method. So yes, I also withdraw my criticism
about
#testNestedUnwind. :-)
But I don't have any good idea for version (ii) yet. Call it
#abandon like
in the debugger? Then again, #abandon is rather a verb from the
Morphic
language. Further possible vocables (according to my synonym
thesaurus)
include #end, #stop, #finish, #unwind, #abort, #exit. Please
help... :-)
I'd probably go with something like #terminateLight because it's a
proper
process termination including unwinds except the ones currently in
progress
- so it is a light version of #terminate :) I've checked
VisualWorks: they
chose #terminateUnsafely for this type of termination which I
don't like
much, it sounds too negative; the real meaning is rather #terminateAsSafelyAsPossibleGivenTheCircumstances ;).
I'm wondering whether #unwindTo: (used ony by Generator) is bugged
(with
regard to dealing with non-local returns), and could be
fixed/unified with
your approach. Look at these examples:
p := [[Processor activeProcess suspend] valueUninterruptably] fork. Processor yield. p suspendedContext unwindTo: nil
or
p := [[:exit | [Processor activeProcess suspend] ensure: [exit
value]]
valueWithExit] fork. Processor yield. p suspendedContext unwindTo: nil
If you do `p terminate` instead of `p suspendedContext unwindTo:
nil`, it
works fine, but #unwindTo causes a block cannot return error - I
think it's
the same bug all over again :) #value evaluates the non-local
return on the
wrong stack...
Regarding our cannot return discussion - I have to think about it
and I'll
post my reply later in [1] to keep it separate :)
Thanks again and regards,
[1]
http://forum.world.st/The-Inbox-Kernel-ct-1405-mcz-td5129706.html#a5130114
^[^ Jaromir
Sent from: http://forum.world.st/Squeak-Dev-f45488.html
Hi Eliot,
Many thanks for your comments; it'll take me some time to respond properly and with reasonable arguments but let me just briefly touch some points I think you may be mistaken (in the text below): best, ~~~ ^[^ Jaromir
Sent from Squeak Inbox Talk
On 2021-12-16T14:20:01-08:00, eliot.miranda@gmail.com wrote:
Hi Jaromir,
I'm responding to this message because you introduce useful
categorisation of different kinds of terminate. I'll respond in context below.
One more naming suggestion: (iii) terminate (~run all unwind blocks) (ii) terminateLessSafely (~skip unwind blocks in progress, run all not-yet-started ones) (i) terminateUnsafely (~kill, run no unwinds)
OK, this is really useful. It provides the straight-forward options we have for process termination. Now let's try and be categorical about unwind blocks.
Unwind blocks are used to maintain invariants. The classic example is Semaphore>>critical:
critical: mutuallyExcludedBlock "Evaluate mutuallyExcludedBlock only if the receiver is not currently in the process of running the critical: message. If the receiver is, evaluate mutuallyExcludedBlock after the other critical: message is finished." <criticalSection> self wait. ^mutuallyExcludedBlock ensure: [self signal]
Given that unwind blocks are used to maintain invariants, any terminate which does (iii), run all unwind blocks, including those that have already been run, is completely broken.
I think this is a misunderstanding: it was never intended to run an unwind block twice; the point was to *finish* unwind blocks that were in the middle of their execution; never re-run.
We can therefore ignore (i). It would run unwinds more than once. The correct terminate is (ii). Skip unwind blocks already in progress. Presumably something very wrong has happened in an unwind block that has not completed. But all unwinds further away from top of stack should and must be run normally. Note that in the normal case there will be no such partial unwinds; it will not occur in correct code. Note further that its extremely unlikely that there will be an unwind within an unwind.
But there's an assumption here. In normal termination, unwind blocks will just run, and the process will terminate cleanly. If it terminates itself then there is no issue other than potential bugs in unwinds (likely during development, rather than normal running). An application that wants to terminate other processes should be written to terminate those processes it needs to only when those processes are at some safe point. I would not spend effort trying to make terminate safe when sent from other processes at arbitrary points. That counts as trying to shoot oneself in the foot.
Fair point, I did it as a learning exercise :)
So terminate boils down to two or three cases, depending on how you look at it. The first case, or pair of cases, is a process that terminates itself or is terminated by another process while at a safe point. In this case any and all unwinds *must* be run. The final case is a process that is stuck in termination, presumably due to a bug in an unwind. This is the only point where we have the choice between ii and i.
So IMO the choice should be between terminate (ii) and terminateUnsafely (i). [I believe that in VisualWorks I called (i) terminateWithExtremePrejudice, after Apocalypse Now].
Current VisualWorks use all three termination modes and unfortunately renamed them to boring: (iii) terminate (ii) terminateUnsafely (i) terminateUnsafelyNow
The semantics is very similar to what's being proposed here though, i.e. #terminate tries to finish all *unfinished* unwind blocks in progress (even nested). #terminateUnsafely unwinds only blocks that haven't started yet and is used as a debugger Abandon action and #terminateUnsafelyNow just kills.
When a process starts to terminate, it is marked somehow as being terminated, running unwind blocks. If the normal terminate is sent to the process while in this state that terminate send should raise an error; attempting to terminate a process twice is an error.
Yes, this another bug I found in current #terminate; it allows teminating again a process being currently terminated and also allows to resume a process that is being terminated. I'll be sending some tests and suggestion how to fix it.
We cannot automatically decide if a process in this state is correctly running unwinds; that would be equivalent to solving the halting problem. Therefore we leave it up to the programmer to decide if, on discovering a terminating process stuck somewhere in running unwinds, they terminateUnsafely.
A key attitude here is to make the common case work well. Don't let the perfect be the enemy of the good. In this case there is no perfect solution; arbitrary errors *can* occur, *potentially*, in termination, but in practice are extremely rare. Keeping the system simple, comprehensible and maintainable is a prime directive. If a programmer can, for example, use the debugger to manually run undins that have not yet run because of a stuck unwind, then there is no absolute need for the system to handle this situation gracefully. A developer that gets themselves in hot water creating errors iu unwinds can get themselves out using the debugger.
So for me, KISS.
Agreed, absolutely... The only BUT from me is to fix bugs making learning a miserable experience :)
Just support something that guards against multiple termination,
As I said, I'll send tests and a solution for your consideration.
and in the debugger, reveals clearly where a process is in the course of running unwinds.
And I really *don'* like the idea of running terminate in a separate process.
I'm sorry to hear that indeed :D
This has bad performance implications. Most terminations can be done in the process itself; that should be the expectation.
Marcel wrote:
How would you summarize the differences between #terminate,
#terminateAggressively, and #kill, each with 1-2 sentences? :-)
Inspired by VW:
(iii) "Terminate the process and allow all unwind blocks to run, even if they are currently in progress." This is the behavior we expect when terminating a healthy process.
(ii) "Terminate the process and run all not-yet-started unwind blocks; if the process is in the middle of an unwind block, then that unwind block will not be completed, but subsequent unwind blocks will be run." This is the behavior we expect when we close the debugger in a situation when the unwind block may not be able to complete, either because of an error, or because it is hung. In most other circumstances, we would want to use #terminate, which allows unwind blocks in progress to complete.
(i) "Terminate the process without attempting to run any of its unwind blocks." This is the behavior we might need in very rare circumstances when modes (iii) and (ii) fail.
Changeset:
Please find the attached changeset in which I have changed the names
accordingly and also have removed the kill/destroy operation from the UI as per request of Marcel. I aggree with his argument that destroy is a kind of private operation that affects the interna of the process and should be only used for troubleshooting, so we should not give this hack too much visibility.
Nice! I liked your Abandon button with options more but this is a start. Kill option: do you mean no access to the kill option from the Process browser either? Process browser is a very troubleshooting tool :)
And finally, If I may - I'd slightly prefer not adding another #terminate: to the Process class only to serve a single sender but rather inline it and address the two termination modes directly in the ProcessBrowser class:
ProcessBrowser class >> #terminateProcess: aProcess aggressively: aggressive
aProcess ifNil: [^ self]. self suspendedProcesses removeKey: aProcess ifAbsent: [].
aProcess terminate: aggressive.
aggressive ifFalse: [aProcess terminate] ifTrue: [aProcess terminateAggressively]
Concerning your #unwindTo: issue - not sure if this actually related to
this thread? Actually I've lost the overview which terminate mode - (ii) or (iii) - you are referring to, maybe we should discuss this in a separate conversation. :-)
Yes, it's a bug and deserves its own thread.
=== Summary:
TLDR: Concerning our next steps - consider this a small to-do list:
- [ ] We want to implement three modi of process termination:
- [x] (i) is not yet implemented but should be easy to implement
- [x] (ii) is in my proposal from [2], maybe needs further refinement
- [ ] (iii) is pretty much what we have in Process >> #terminate in
the current Trunk. Is there anything still missing yet?
- [ ] Decide on names for (ii) and (iii) - see above
- [ ] Decide on UI integration into debugger - see above
Add this one?
- [ ] Decide on UI integration into Process Browser
Regarding Process >> #terminate - the current Trunk version is not final; the final version is the Inbox's Kernel-jar.1414.
More or less done! :)
Thanks,
Jaromir
On 2021-08-30T12:51:21+02:00, christoph.thiede at student.hpi.uni-potsdam.de wrote:
Hi Marcel, hi all,
How would you summarize the differences between #terminate,
#terminateAggressively, and #kill, each with 1-2 sentences? :-)
Let's keep using the numbers from above instead, as the names are only
my imperfect proposals:
(i) run no unwind contexts (harshest possible way; currently only
achievable by doing "suspendedContext privSender: nil" prior to terminating). You would need this very rarely, if at all, for troubleshooting.
(ii) run not-yet started unwind contexts (this is what I proposed in
fix-Process-terminate.1.cs [1]). This is the behavior we expect from the Abandon button in a debugger. I think?
(iii) run all unwind contexts, including those that already have been
started (this is the most friendly way that you implemented in #terminate recently). This is the behavior we expect when terminating a healthy process.
Here are some ideas for better names:
(i) #destroy (as opposed to #kill, this sounds more like the cheap and
unreactable operation it is)
(ii) #terminateAggressively, which redirects to #terminate: true (iii) #terminate, which redirects to #terminate: false
Please find the attached changeset in which I have changed the names
accordingly and also have removed the kill/destroy operation from the UI as per request of Marcel. I aggree with his argument that destroy is a kind of private operation that affects the interna of the process and should be only used for troubleshooting, so we should not give this hack too much visibility.
Best, Christoph
Sent from Squeak Inbox Talk
On 2021-08-23T13:59:04+02:00, marcel.taeumel at hpi.de wrote:
Hi Christoph --
How would you summarize the differences between #terminate,
#terminateAggressively, and #kill, each with 1-2 sentences? :-)
Best, Marcel Am 23.08.2021 13:18:40 schrieb christoph.thiede at
student.hpi.uni-potsdam.de <christoph.thiede at student.hpi.uni-potsdam.de
:
Hi all, hi Jaromir,
based on the to-do list from my previous message, I have created a
changeset that adds Process >> #terminateAggressively (ii) and Process >> #kill (i) and integrates them into the process browser and the debugger. Most importantly, the following snippet will inform "nil" now instead of "ZeroDevision" after abandoning the error:
����[[x1 := nil] ensure: [x1 := (2 / 0)]] ��������ensure: [self inform: x1 asString].
With this changeset, I would be fully happy again with the
functionality of the debugger. :-) What remains to do is the following:
- [ ] We want to implement three modi of process termination:
- [x] (i) - see #kill
- [X] (ii) - see #terminateAggressively
- [ ] (iii) is pretty much what we have in Process >> #terminate in
the current Trunk. Is there anything still missing yet?
- [ ] Decide on names for (ii) and (iii) - see my previous message
- [ ] Decide on UI integration into debugger - proposal exists in
Process-terminateAggressively.1, but open for criticism
- [ ] Test #terminateAggressively (actualy, I don't have a good of
overview of all your new #terminate tests - probably it would be easiest to wait until they have arrived in the Trunk and then create copies of them to test #terminateAggressively analogously :D)
Best, Christoph
Sent from Squeak Inbox Talk [
https://github.com/hpi-swa-lab/squeak-inbox-talk]
On 2021-08-22T16:07:54+02:00, christoph.thiede at
student.hpi.uni-potsdam.de wrote:
Hi Jaromir,
back from vacation, *finally* I have found some time to return back
to this pleasant problem! :-)
Great, it sounds like we're already agreeing pretty much, just let
me address some small points:
Debugger - Abandon could be the lightweight version you proposed.
Why not
have a proper Abandon button for it? ����The right click menu on a context could offer the Kill option
(next to
'peel to first like this'); no button necessary. Now the question is what should be under the "window close"
red-circle-x -
heavyweight terminate? I'm thinking this scenario: if the debugger
returns
after closing the window you start thinking what happened and use
Abandon;
if it still doesn't help you go right-click and kill it?
My usual scenario is (limited experience however): look at
something in the
debugger (on a healthy process) and close the window (i.e. full
termination
is appropriate and I'd even say preferable). If something goes
wrong - then
I'd welcome a hint there are options - thus the proper Abandon
button - what
do you think?
Hm, I would rather say that closing the window should have the same
effect as pressing "abandon" - personally, I am very much used to this and we do not really have good options to communicate a different behavior of the close button. I think presenting the new behaviors Kill (i) and (iii) in some kind of menu would be less confusing.
A kill item in the stack trace menu sounds fine for now (even though
killing is not actually related to the selected context). In the long term, I would like to introduce menu buttons in the fashion of [1] in the Morphic UI (Marcel is currently reviewing my proposal for this), then we could move Kill into the Abandon menu, but for now, your proposal sounds perfectly fine to me. :-)
look at something in the debugger (on a healthy process) and close
the window (i.e. full termination is appropriate and I'd even say preferable).
Can you help me please, if the process is healthy (so no unwind
context is lying on the stack), would there be any difference between the different terminate modi (ii) and (iii)?
> But I don't have any good idea for version (ii) yet. Call it
#abandon like
> in the debugger? Then again, #abandon is rather a verb from the
Morphic
> language. Further possible vocables (according to my synonym
thesaurus)
> include #end, #stop, #finish, #unwind, #abort, #exit. Please
help... :-)
I'd probably go with something like #terminateLight because it's a
proper
process termination including unwinds except the ones currently in
progress
- so it is a light version of #terminate :) I've checked
VisualWorks: they
chose #terminateUnsafely for this type of termination which I
don't like
much, it sounds too negative; the real meaning is rather #terminateAsSafelyAsPossibleGivenTheCircumstances ;).
Hm, "light" reminds me of "friendly" and (iii) would certainly be
more friendly (giving the process more freedom for cleaning things up). Difficult ...
What about #terminateNow for (ii) and #terminate for (iii), slightly
related to #updateStyle and #updateStyleNow? Since (iii) might continue with the execution of the current halfway-executed context, (ii) is more urgent.
Another option would be "terminate: aggressive", analogously to
"cleanup: aggressive" on Behavior where #terminate would fall back to "terminate: false". But this would make the new mode a little bit worse accessible.
Concerning your #unwindTo: issue - not sure if this actually related
to this thread? Actually I've lost the overview which terminate mode - (ii) or (iii) - you are referring to, maybe we should discuss this in a separate conversation. :-)
TLDR: Concerning our next steps - consider this a small to-do list:
- [ ] We want to implement three modi of process termination:
����- [ ] (i) is not yet implemented but should be easy to implement ����- [ ] (ii) is in my proposal from [2], maybe needs further
refinement
����- [ ] (iii) is pretty much what we have in Process >> #terminate
in the current Trunk. Is there anything still missing yet?
- [ ] Decide on names for (ii) and (iii) - see above
- [ ] Decide on UI integration into debugger - see above
Further answers following soon.
Best, Christoph
[1]
https://user-images.githubusercontent.com/48567372/60924070-4472fe80-a26e-11...
[2]
http://lists.squeakfoundation.org/pipermail/squeak-dev/2021-May/215608.html (fix-Process-terminate.cs)
Sent from Squeak Inbox Talk
On 2021-05-29T14:31:13-05:00, m at jaromir.net wrote:
Hi Christoph,
> Jaromir, your proposal to provide multiple selectors for
modeling separate
> modes of termination sounds like a very good idea to me. But how
many
> different modes do we actually need? So far I can count three
modes:
> > (i) run no unwind contexts (harshest possible way; currently only > achievable by doing "suspendedContext privSender: nil" prior to > terminating) > (ii) run not-yet started unwind contexts (this is what I
proposed in
> fix-Process-terminate.1.cs [1]) > (iii) run all unwind contexts, including those that already have
been
> started (this is the most friendly way that you implemented in
#terminate
> recently)
I think this is it.
Litereally minutes ago had to use privSender: nil to get rid of a
debugger
:) Fully terminate really is too strong to recover from fatal
errors.
> ... my point here is: Proceeding from an error almost always
doesn't seem
> "right". :-) It is always a decision by the debugging programmer
to
> override the default control flow and switch to the "next
plausible
> alternative control flow", i.e., resume as if the error would
have never
> been raised.
yes - I'd add: even an error may quite often be completely benign,
like
'Transcript show: 1/0' - possibly a typo so you just may want to
Proceed or
fully terminate. In case the error damages a whole subsequent
chain of
events, you're absolutely right a full termination seems a silly
option and
a light version of terminate may be the most appropriate.
So I fully agree the decision which termination mode it is stays
with the
user - so I'm all for giving the user the choices you suggested.
> \1. Which mode should we use in which situations? > > I think this debate could benefit from a few more concrete usage > scenarios. I'm just collecting some here (thinking aloud): > > - Process Browser: We can provide multiple options in the
process menu.
> - Debugger: I agree with you that Abandon should always run
not-yet
> started unwind contexts but never resume halfway-executed unwind
contexts.
> So this maps to to mode (ii) from above. > - Skimming through most senders of #terminate in the image,
they often
> orchestrate helper processes, deal with unhandled errors or
timeouts, or
> do similar stuff - usually they should be very fine with the
friendly
> version of #terminate, i.e. mode (iii) from above. I think. > - Regarding option (1), I think you would need it extremely
seldom but
> maybe in situations like when your stack contains a loop, your
unwind
> contexts will cause a recursion/new error, or you deliberately
want to
> prevent any unwind context from running. No objections against
adding a
> small but decent button for this in the debugger. :-) > > Would you agree with these behaviors? Maybe you can add further
examples
> to the list?
Yes
Process Browser - the right click menu could provide all options
Debugger - Abandon could be the lightweight version you proposed.
Why not
have a proper Abandon button for it? ����The right click menu on a context could offer the Kill option
(next to
'peel to first like this'); no button necessary. ����Now the question is what should be under the "window close"
red-circle-x -
heavyweight terminate? I'm thinking this scenario: if the debugger
returns
after closing the window you start thinking what happened and use
Abandon;
if it still doesn't help you go right-click and kill it?
My usual scenario is (limited experience however): look at
something in the
debugger (on a healthy process) and close the window (i.e. full
termination
is appropriate and I'd even say preferable). If something goes
wrong - then
I'd welcome a hint there are options - thus the proper Abandon
button - what
do you think?
> \2. How should we name them? > > Direct proposal: (i) #kill and (iii) #terminate. > After looking up the original behavior of #terminate in Squeak
5.3, I
> think it would be consistent to resume all halfway-executed
unwind
> contexts in this method. So yes, I also withdraw my criticism
about
> #testNestedUnwind. :-) > > But I don't have any good idea for version (ii) yet. Call it
#abandon like
> in the debugger? Then again, #abandon is rather a verb from the
Morphic
> language. Further possible vocables (according to my synonym
thesaurus)
> include #end, #stop, #finish, #unwind, #abort, #exit. Please
help... :-)
I'd probably go with something like #terminateLight because it's a
proper
process termination including unwinds except the ones currently in
progress
- so it is a light version of #terminate :) I've checked
VisualWorks: they
chose #terminateUnsafely for this type of termination which I
don't like
much, it sounds too negative; the real meaning is rather #terminateAsSafelyAsPossibleGivenTheCircumstances ;).
I'm wondering whether #unwindTo: (used ony by Generator) is bugged
(with
regard to dealing with non-local returns), and could be
fixed/unified with
your approach. Look at these examples:
p := [[Processor activeProcess suspend] valueUninterruptably] fork. Processor yield. p suspendedContext unwindTo: nil
or
p := [[:exit | [Processor activeProcess suspend] ensure: [exit
value]]
valueWithExit] fork. Processor yield. p suspendedContext unwindTo: nil
If you do `p terminate` instead of `p suspendedContext unwindTo:
nil`, it
works fine, but #unwindTo causes a block cannot return error - I
think it's
the same bug all over again :) #value evaluates the non-local
return on the
wrong stack...
Regarding our cannot return discussion - I have to think about it
and I'll
post my reply later in [1] to keep it separate :)
Thanks again and regards,
[1]
http://forum.world.st/The-Inbox-Kernel-ct-1405-mcz-td5129706.html#a5130114
^[^ Jaromir
Sent from: http://forum.world.st/Squeak-Dev-f45488.html
Hi Eliot,
since you replied to Jaromir's message from 2021-11-15 18:33:33 rather than to the newest message, please let me just make sure that you have read my announcement about Kernel-ct.1434 & Co. which I uploaded to the Trunk yesterday. I hope you do not feel left out with the design decisions which I implemented, but there was a long time no reaction from you (this is not a complain) on this topic (or did I overlook any post by you?) and also, Marcel had agreed to the plan.
Most importantly, (i) (no unwinding) is now called #destroy and (ii) (execute all not-yet started unwind contexts) is called #terminateAggressively. Debugger's Abandon uses #terminateAggressively now, I will merge Jaromir's changes to #terminate soon so that halfway-executed unwind contexts are resumed correctly during #terminate.
An application that wants to terminate other processes should be written to terminate those processes it needs to only when those processes are at some safe point. I would not spend effort trying to make terminate safe when sent from other processes at arbitrary points. That counts as trying to shoot oneself in the foot.
Aha! So you would not pose the "thread-safe" requirement to #terminate? That is interesting to hear - personally, I would have always relied on this assumption, and I would not be astonished if we already violating this assumption somewhere in the Trunk right now. :-) My mental model of processes in Squeak was rather an unstructed heap than a kind of "ownership tree" of all processes that are allowed for termination. Given that, I will at least not complain if Jaromir continues his efforts on improving #terminate but see chances to improve the system robustness. Maybe we should even use a semaphore to make termination thread-safe?
So terminate boils down to two or three cases, depending on how you look at it. The first case, or pair of cases, is a process that terminates itself or is terminated by another process while at a safe point. In this case any and all unwinds *must* be run. The final case is a process that is stuck in termination, presumably due to a bug in an unwind. This is the only point where we have the choice between ii and i.
I think that there is room for another case (or did you include this into your first case)? Let process A being in the middle of an unwind context right now (while returning from a context regulary, i.e., without termination), when process B (maybe with a higher priority) interrupts it and sends #terminate to A. I think this is exactly the scenario what this thread is about. In this case, #terminate must recognize that A is already in the middle of an unwind context and complete this unwind context rather than dropping it and starting with the next one. This is the central difference to #terminateAggressively where the primary assumption is that the top context will never be continued under any circumstances. We expect this behavior in the debugger where we need full control over the active context.
Best, Christoph
--- Sent from Squeak Inbox Talk
On 2021-12-16T14:20:01-08:00, eliot.miranda@gmail.com wrote:
Hi Jaromir,
I'm responding to this message because you introduce useful
categorisation of different kinds of terminate. I'll respond in context below.
On Mon, Nov 15, 2021 at 9:41 AM <mail at jaromir.net> wrote:
Hi Christoph,
Once again, apologies for the huge delay in my responding :)
Hm, I would rather say that closing the window should have the same
effect as pressing "abandon" - personally, I am very much used to this and we do not really have good options to communicate a different behavior of the close button.
Yes, you're right indeed.
A kill item in the stack trace menu sounds fine for now (even though
killing is not actually related to the selected context). In the long term, I would like to introduce menu buttons in the fashion of [1] in the Morphic UI.
Your Abandon button with options is terrific, please disregard my suggestion :)
look at something in the debugger (on a healthy process) and close the
window (i.e. full termination is appropriate and I'd even say preferable).
Can you help me please, if the process is healthy (so no unwind context
is lying on the stack), would there be any difference between the different terminate modi (ii) and (iii)?
I thought there was a difference, e.g. if you debug #ensure on a healthy process and want to abandon the debugger in the middle - does it make sense?
Example: [^2] ensure: [Transcript cr; show: 'done'. "do some cleaning"]
Now, you start a debugger, do a few steps and in the middle of the ensure block you decide you've seen enough and hit Abandon - if it's implemented with (ii), i.e. the 'aggressive', less safe version of terminate, the block won't get completed. If it contained some cleaning procedures, the cleaning wouldn't be executed... That's why I'm still leaning slightly towards full termination as a default Abandon action - but e.g. Visual Works use the less safe termination in this case, as you suggest. Other Smalltalks I checked (Cuis, Pharo and VA) don't have three ways to terminate at all (yet) :) However, ultimately users that use Squeak the most should have the final say. I'll be happy with both options. (Or make it a preference?).
[[x1 := nil] ensure: [x1 := (2 / 0)]] ensure: [self inform: x1 asString].
Hmm, this example of your nicely justifies your approach: Abandon = (ii) :D
Naming:
Hm, "light" reminds me of "friendly" and (iii) would certainly be more
friendly (giving the process more freedom for cleaning things up). Difficult ...
What about #terminateNow for (ii) and #terminate for (iii), slightly
related to #updateStyle and #updateStyleNow? Since (iii) might continue with the execution of the current halfway-executed context, (ii) is more urgent.
Another option would be "terminate: aggressive", analogously to
"cleanup: aggressive" on Behavior where #terminate would fall back to "terminate: false". But this would make the new mode a little bit worse accessible.
Agreed, "light" is not good. Checked Visual Works again and they use terminate for (iii), terminateUnsafely for (ii) and terminateUnsafelyNow for kill (i). #teminateNow suggests it would terminate now as opposed to later :) No hint about the nature of the procedure that will attempt to terminate with increased level of possible damage to the environment... But we're getting closer and again, I'll be happy with either of them :)
Here are some ideas for better names:
(i) #destroy (as opposed to #kill, this sounds more like the cheap and
unreactable operation it is)
(ii) #terminateAggressively, which redirects to #terminate: true (iii) #terminate, which redirects to #terminate: false
One more naming suggestion: (iii) terminate (~run all unwind blocks) (ii) terminateLessSafely (~skip unwind blocks in progress, run all not-yet-started ones) (i) terminateUnsafely (~kill, run no unwinds)
OK, this is really useful. It provides the straight-forward options we have for process termination. Now let's try and be categorical about unwind blocks.
Unwind blocks are used to maintain invariants. The classic example is Semaphore>>critical:
critical: mutuallyExcludedBlock "Evaluate mutuallyExcludedBlock only if the receiver is not currently in the process of running the critical: message. If the receiver is, evaluate mutuallyExcludedBlock after the other critical: message is finished." <criticalSection> self wait. ^mutuallyExcludedBlock ensure: [self signal]
Given that unwind blocks are used to maintain invariants, any terminate which does (iii), run all unwind blocks, including those that have already been run, is completely broken. We can therefore ignore (i). It would run unwinds more than once. The correct terminate is (ii). Skip unwind blocks already in progress. Presumably something very wrong has happened in an unwind block that has not completed. But all unwinds further away from top of stack should and must be run normally. Note that in the normal case there will be no such partial unwinds; it will not occur in correct code. Note further that its extremely unlikely that there will be an unwind within an unwind.
But there's an assumption here. In normal termination, unwind blocks will just run, and the process will terminate cleanly. If it terminates itself then there is no issue other than potential bugs in unwinds (likely during development, rather than normal running). An application that wants to terminate other processes should be written to terminate those processes it needs to only when those processes are at some safe point. I would not spend effort trying to make terminate safe when sent from other processes at arbitrary points. That counts as trying to shoot oneself in the foot.
So terminate boils down to two or three cases, depending on how you look at it. The first case, or pair of cases, is a process that terminates itself or is terminated by another process while at a safe point. In this case any and all unwinds *must* be run. The final case is a process that is stuck in termination, presumably due to a bug in an unwind. This is the only point where we have the choice between ii and i.
So IMO the choice should be between terminate (ii) and terminateUnsafely (i). [I believe that in VisualWorks I called (i) terminateWithExtremePrejudice, after Apocalypse Now]. When a process starts to terminate, it is marked somehow as being terminated, running unwind blocks. If the normal terminate is sent to the process while in this state that terminate send should raise an error; attempting to terminate a process twice is an error. We cannot automatically decide if a process in this state is correctly running unwinds; that would be equivalent to solving the halting problem. Therefore we leave it up to the programmer to decide if, on discovering a terminating process stuck somewhere in running unwinds, they terminateUnsafely.
A key attitude here is to make the common case work well. Don't let the perfect be the enemy of the good. In this case there is no perfect solution; arbitrary errors *can* occur, *potentially*, in termination, but in practice are extremely rare. Keeping the system simple, comprehensible and maintainable is a prime directive. If a programmer can, for example, use the debugger to manually run undins that have not yet run because of a stuck unwind, then there is no absolute need for the system to handle this situation gracefully. A developer that gets themselves in hot water creating errors iu unwinds can get themselves out using the debugger.
So for me, KISS. Just support something that guards against multiple termination, and in the debugger, reveals clearly where a process is in the course of running unwinds.
And I really *don'* like the idea of running terminate in a separate process. This has bad performance implications. Most terminations can be done in the process itself; that should be the expectation.
Marcel wrote:
How would you summarize the differences between #terminate,
#terminateAggressively, and #kill, each with 1-2 sentences? :-)
Inspired by VW:
(iii) "Terminate the process and allow all unwind blocks to run, even if they are currently in progress." This is the behavior we expect when terminating a healthy process.
(ii) "Terminate the process and run all not-yet-started unwind blocks; if the process is in the middle of an unwind block, then that unwind block will not be completed, but subsequent unwind blocks will be run." This is the behavior we expect when we close the debugger in a situation when the unwind block may not be able to complete, either because of an error, or because it is hung. In most other circumstances, we would want to use #terminate, which allows unwind blocks in progress to complete.
(i) "Terminate the process without attempting to run any of its unwind blocks." This is the behavior we might need in very rare circumstances when modes (iii) and (ii) fail.
Changeset:
Please find the attached changeset in which I have changed the names
accordingly and also have removed the kill/destroy operation from the UI as per request of Marcel. I aggree with his argument that destroy is a kind of private operation that affects the interna of the process and should be only used for troubleshooting, so we should not give this hack too much visibility.
Nice! I liked your Abandon button with options more but this is a start. Kill option: do you mean no access to the kill option from the Process browser either? Process browser is a very troubleshooting tool :)
And finally, If I may - I'd slightly prefer not adding another #terminate: to the Process class only to serve a single sender but rather inline it and address the two termination modes directly in the ProcessBrowser class:
ProcessBrowser class >> #terminateProcess: aProcess aggressively: aggressive
aProcess ifNil: [^ self]. self suspendedProcesses removeKey: aProcess ifAbsent: [].
aProcess terminate: aggressive.
aggressive ifFalse: [aProcess terminate] ifTrue: [aProcess terminateAggressively]
Concerning your #unwindTo: issue - not sure if this actually related to
this thread? Actually I've lost the overview which terminate mode - (ii) or (iii) - you are referring to, maybe we should discuss this in a separate conversation. :-)
Yes, it's a bug and deserves its own thread.
=== Summary:
TLDR: Concerning our next steps - consider this a small to-do list:
- [ ] We want to implement three modi of process termination:
- [x] (i) is not yet implemented but should be easy to implement
- [x] (ii) is in my proposal from [2], maybe needs further refinement
- [ ] (iii) is pretty much what we have in Process >> #terminate in
the current Trunk. Is there anything still missing yet?
- [ ] Decide on names for (ii) and (iii) - see above
- [ ] Decide on UI integration into debugger - see above
Add this one?
- [ ] Decide on UI integration into Process Browser
Regarding Process >> #terminate - the current Trunk version is not final; the final version is the Inbox's Kernel-jar.1414.
More or less done! :)
Thanks,
Jaromir
On 2021-08-30T12:51:21+02:00, christoph.thiede at student.hpi.uni-potsdam.de wrote:
Hi Marcel, hi all,
How would you summarize the differences between #terminate,
#terminateAggressively, and #kill, each with 1-2 sentences? :-)
Let's keep using the numbers from above instead, as the names are only
my imperfect proposals:
(i) run no unwind contexts (harshest possible way; currently only
achievable by doing "suspendedContext privSender: nil" prior to terminating). You would need this very rarely, if at all, for troubleshooting.
(ii) run not-yet started unwind contexts (this is what I proposed in
fix-Process-terminate.1.cs [1]). This is the behavior we expect from the Abandon button in a debugger. I think?
(iii) run all unwind contexts, including those that already have been
started (this is the most friendly way that you implemented in #terminate recently). This is the behavior we expect when terminating a healthy process.
Here are some ideas for better names:
(i) #destroy (as opposed to #kill, this sounds more like the cheap and
unreactable operation it is)
(ii) #terminateAggressively, which redirects to #terminate: true (iii) #terminate, which redirects to #terminate: false
Please find the attached changeset in which I have changed the names
accordingly and also have removed the kill/destroy operation from the UI as per request of Marcel. I aggree with his argument that destroy is a kind of private operation that affects the interna of the process and should be only used for troubleshooting, so we should not give this hack too much visibility.
Best, Christoph
Sent from Squeak Inbox Talk
On 2021-08-23T13:59:04+02:00, marcel.taeumel at hpi.de wrote:
Hi Christoph --
How would you summarize the differences between #terminate,
#terminateAggressively, and #kill, each with 1-2 sentences? :-)
Best, Marcel Am 23.08.2021 13:18:40 schrieb christoph.thiede at
student.hpi.uni-potsdam.de <christoph.thiede at student.hpi.uni-potsdam.de
:
Hi all, hi Jaromir,
based on the to-do list from my previous message, I have created a
changeset that adds Process >> #terminateAggressively (ii) and Process >> #kill (i) and integrates them into the process browser and the debugger. Most importantly, the following snippet will inform "nil" now instead of "ZeroDevision" after abandoning the error:
????[[x1 := nil] ensure: [x1 := (2 / 0)]] ????????ensure: [self inform: x1 asString].
With this changeset, I would be fully happy again with the
functionality of the debugger. :-) What remains to do is the following:
- [ ] We want to implement three modi of process termination:
- [x] (i) - see #kill
- [X] (ii) - see #terminateAggressively
- [ ] (iii) is pretty much what we have in Process >> #terminate in
the current Trunk. Is there anything still missing yet?
- [ ] Decide on names for (ii) and (iii) - see my previous message
- [ ] Decide on UI integration into debugger - proposal exists in
Process-terminateAggressively.1, but open for criticism
- [ ] Test #terminateAggressively (actualy, I don't have a good of
overview of all your new #terminate tests - probably it would be easiest to wait until they have arrived in the Trunk and then create copies of them to test #terminateAggressively analogously :D)
Best, Christoph
Sent from Squeak Inbox Talk [
https://github.com/hpi-swa-lab/squeak-inbox-talk]
On 2021-08-22T16:07:54+02:00, christoph.thiede at
student.hpi.uni-potsdam.de wrote:
Hi Jaromir,
back from vacation, *finally* I have found some time to return back
to this pleasant problem! :-)
Great, it sounds like we're already agreeing pretty much, just let
me address some small points:
Debugger - Abandon could be the lightweight version you proposed.
Why not
have a proper Abandon button for it? ????The right click menu on a context could offer the Kill option
(next to
'peel to first like this'); no button necessary. Now the question is what should be under the "window close"
red-circle-x -
heavyweight terminate? I'm thinking this scenario: if the debugger
returns
after closing the window you start thinking what happened and use
Abandon;
if it still doesn't help you go right-click and kill it?
My usual scenario is (limited experience however): look at
something in the
debugger (on a healthy process) and close the window (i.e. full
termination
is appropriate and I'd even say preferable). If something goes
wrong - then
I'd welcome a hint there are options - thus the proper Abandon
button - what
do you think?
Hm, I would rather say that closing the window should have the same
effect as pressing "abandon" - personally, I am very much used to this and we do not really have good options to communicate a different behavior of the close button. I think presenting the new behaviors Kill (i) and (iii) in some kind of menu would be less confusing.
A kill item in the stack trace menu sounds fine for now (even though
killing is not actually related to the selected context). In the long term, I would like to introduce menu buttons in the fashion of [1] in the Morphic UI (Marcel is currently reviewing my proposal for this), then we could move Kill into the Abandon menu, but for now, your proposal sounds perfectly fine to me. :-)
look at something in the debugger (on a healthy process) and close
the window (i.e. full termination is appropriate and I'd even say preferable).
Can you help me please, if the process is healthy (so no unwind
context is lying on the stack), would there be any difference between the different terminate modi (ii) and (iii)?
> But I don't have any good idea for version (ii) yet. Call it
#abandon like
> in the debugger? Then again, #abandon is rather a verb from the
Morphic
> language. Further possible vocables (according to my synonym
thesaurus)
> include #end, #stop, #finish, #unwind, #abort, #exit. Please
help... :-)
I'd probably go with something like #terminateLight because it's a
proper
process termination including unwinds except the ones currently in
progress
- so it is a light version of #terminate :) I've checked
VisualWorks: they
chose #terminateUnsafely for this type of termination which I
don't like
much, it sounds too negative; the real meaning is rather #terminateAsSafelyAsPossibleGivenTheCircumstances ;).
Hm, "light" reminds me of "friendly" and (iii) would certainly be
more friendly (giving the process more freedom for cleaning things up). Difficult ...
What about #terminateNow for (ii) and #terminate for (iii), slightly
related to #updateStyle and #updateStyleNow? Since (iii) might continue with the execution of the current halfway-executed context, (ii) is more urgent.
Another option would be "terminate: aggressive", analogously to
"cleanup: aggressive" on Behavior where #terminate would fall back to "terminate: false". But this would make the new mode a little bit worse accessible.
Concerning your #unwindTo: issue - not sure if this actually related
to this thread? Actually I've lost the overview which terminate mode - (ii) or (iii) - you are referring to, maybe we should discuss this in a separate conversation. :-)
TLDR: Concerning our next steps - consider this a small to-do list:
- [ ] We want to implement three modi of process termination:
????- [ ] (i) is not yet implemented but should be easy to implement ????- [ ] (ii) is in my proposal from [2], maybe needs further
refinement
????- [ ] (iii) is pretty much what we have in Process >> #terminate
in the current Trunk. Is there anything still missing yet?
- [ ] Decide on names for (ii) and (iii) - see above
- [ ] Decide on UI integration into debugger - see above
Further answers following soon.
Best, Christoph
[1]
https://user-images.githubusercontent.com/48567372/60924070-4472fe80-a26e-11...
[2]
http://lists.squeakfoundation.org/pipermail/squeak-dev/2021-May/215608.html (fix-Process-terminate.cs)
Sent from Squeak Inbox Talk
On 2021-05-29T14:31:13-05:00, m at jaromir.net wrote:
Hi Christoph,
> Jaromir, your proposal to provide multiple selectors for
modeling separate
> modes of termination sounds like a very good idea to me. But how
many
> different modes do we actually need? So far I can count three
modes:
> > (i) run no unwind contexts (harshest possible way; currently only > achievable by doing "suspendedContext privSender: nil" prior to > terminating) > (ii) run not-yet started unwind contexts (this is what I
proposed in
> fix-Process-terminate.1.cs [1]) > (iii) run all unwind contexts, including those that already have
been
> started (this is the most friendly way that you implemented in
#terminate
> recently)
I think this is it.
Litereally minutes ago had to use privSender: nil to get rid of a
debugger
:) Fully terminate really is too strong to recover from fatal
errors.
> ... my point here is: Proceeding from an error almost always
doesn't seem
> "right". :-) It is always a decision by the debugging programmer
to
> override the default control flow and switch to the "next
plausible
> alternative control flow", i.e., resume as if the error would
have never
> been raised.
yes - I'd add: even an error may quite often be completely benign,
like
'Transcript show: 1/0' - possibly a typo so you just may want to
Proceed or
fully terminate. In case the error damages a whole subsequent
chain of
events, you're absolutely right a full termination seems a silly
option and
a light version of terminate may be the most appropriate.
So I fully agree the decision which termination mode it is stays
with the
user - so I'm all for giving the user the choices you suggested.
> \1. Which mode should we use in which situations? > > I think this debate could benefit from a few more concrete usage > scenarios. I'm just collecting some here (thinking aloud): > > - Process Browser: We can provide multiple options in the
process menu.
> - Debugger: I agree with you that Abandon should always run
not-yet
> started unwind contexts but never resume halfway-executed unwind
contexts.
> So this maps to to mode (ii) from above. > - Skimming through most senders of #terminate in the image,
they often
> orchestrate helper processes, deal with unhandled errors or
timeouts, or
> do similar stuff - usually they should be very fine with the
friendly
> version of #terminate, i.e. mode (iii) from above. I think. > - Regarding option (1), I think you would need it extremely
seldom but
> maybe in situations like when your stack contains a loop, your
unwind
> contexts will cause a recursion/new error, or you deliberately
want to
> prevent any unwind context from running. No objections against
adding a
> small but decent button for this in the debugger. :-) > > Would you agree with these behaviors? Maybe you can add further
examples
> to the list?
Yes
Process Browser - the right click menu could provide all options
Debugger - Abandon could be the lightweight version you proposed.
Why not
have a proper Abandon button for it? ????The right click menu on a context could offer the Kill option
(next to
'peel to first like this'); no button necessary. ????Now the question is what should be under the "window close"
red-circle-x -
heavyweight terminate? I'm thinking this scenario: if the debugger
returns
after closing the window you start thinking what happened and use
Abandon;
if it still doesn't help you go right-click and kill it?
My usual scenario is (limited experience however): look at
something in the
debugger (on a healthy process) and close the window (i.e. full
termination
is appropriate and I'd even say preferable). If something goes
wrong - then
I'd welcome a hint there are options - thus the proper Abandon
button - what
do you think?
> \2. How should we name them? > > Direct proposal: (i) #kill and (iii) #terminate. > After looking up the original behavior of #terminate in Squeak
5.3, I
> think it would be consistent to resume all halfway-executed
unwind
> contexts in this method. So yes, I also withdraw my criticism
about
> #testNestedUnwind. :-) > > But I don't have any good idea for version (ii) yet. Call it
#abandon like
> in the debugger? Then again, #abandon is rather a verb from the
Morphic
> language. Further possible vocables (according to my synonym
thesaurus)
> include #end, #stop, #finish, #unwind, #abort, #exit. Please
help... :-)
I'd probably go with something like #terminateLight because it's a
proper
process termination including unwinds except the ones currently in
progress
- so it is a light version of #terminate :) I've checked
VisualWorks: they
chose #terminateUnsafely for this type of termination which I
don't like
much, it sounds too negative; the real meaning is rather #terminateAsSafelyAsPossibleGivenTheCircumstances ;).
I'm wondering whether #unwindTo: (used ony by Generator) is bugged
(with
regard to dealing with non-local returns), and could be
fixed/unified with
your approach. Look at these examples:
p := [[Processor activeProcess suspend] valueUninterruptably] fork. Processor yield. p suspendedContext unwindTo: nil
or
p := [[:exit | [Processor activeProcess suspend] ensure: [exit
value]]
valueWithExit] fork. Processor yield. p suspendedContext unwindTo: nil
If you do `p terminate` instead of `p suspendedContext unwindTo:
nil`, it
works fine, but #unwindTo causes a block cannot return error - I
think it's
the same bug all over again :) #value evaluates the non-local
return on the
wrong stack...
Regarding our cannot return discussion - I have to think about it
and I'll
post my reply later in [1] to keep it separate :)
Thanks again and regards,
[1]
http://forum.world.st/The-Inbox-Kernel-ct-1405-mcz-td5129706.html#a5130114
^[^ Jaromir
Sent from: http://forum.world.st/Squeak-Dev-f45488.html
Hi Eliot,
Thanks again; I'll try to comment your points:
One more naming suggestion: (iii) terminate (~run all unwind blocks) (ii) terminateLessSafely (~skip unwind blocks in progress, run all not-yet-started ones) (i) terminateUnsafely (~kill, run no unwinds)
OK, this is really useful. It provides the straight-forward options we have for process termination. Now let's try and be categorical about unwind blocks.
Unwind blocks are used to maintain invariants. The classic example is Semaphore>>critical:
critical: mutuallyExcludedBlock "Evaluate mutuallyExcludedBlock only if the receiver is not currently in the process of running the critical: message. If the receiver is, evaluate mutuallyExcludedBlock after the other critical: message is finished." <criticalSection> self wait. ^mutuallyExcludedBlock ensure: [self signal]
Given that unwind blocks are used to maintain invariants, any terminate which does (iii), run all unwind blocks, including those that have already been run, is completely broken. We can therefore ignore (i). It would run unwinds more than once. The correct terminate is (ii). Skip unwind blocks already in progress. Presumably something very wrong has happened in an unwind block that has not completed.
Not necessarily; how about we just debug a completely healthy process until inside an unwind block and then we decide to quit debugging - we should be able to fully terminate and expect the unfinished ensure blocks unwind correctly (there's an option in the Debugger to use classic terminate (iii) instead of the default (ii) now). Another good example presented Christoph in his reply.
But all unwinds further away from top of stack should and must be run normally. Note that in the normal case there will be no such partial unwinds; it will not occur in correct code. Note further that its extremely unlikely that there will be an unwind within an unwind.
True - except people learning about processes and debugging their crazy examples :) I remember my desperation when nothing made sense and the examples kept crashing. So even if this makes no sense for real-life applications, I'd like to consider it important as a learning tool (and I believe Squeak is a nice tool for education purposes).
But there's an assumption here. In normal termination, unwind blocks will just run, and the process will terminate cleanly. If it terminates itself then there is no issue other than potential bugs in unwinds (likely during development, rather than normal running). An application that wants to terminate other processes should be written to terminate those processes it needs to only when those processes are at some safe point. I would not spend effort trying to make terminate safe when sent from other processes at arbitrary points. That counts as trying to shoot oneself in the foot.
There are quite a few senders of #terminate; they usually check if the process to be terminated is not nil but rarely, if ever, they check whether the process is terminated; I assume these are the "safe point" situations where the programmer presumes he has sufficient knowledge about the process to be terminated.
A scenario I tried to address in the new #terminate is clearly the "shoot oneself in the foot" case :) I thought it might e.g. help designing programs running concurrent cooperating processes including occasional forking and terminating... hmm, that may sound too bold :)
So terminate boils down to two or three cases, depending on how you look at it. The first case, or pair of cases, is a process that terminates itself or is terminated by another process while at a safe point. In this case any and all unwinds *must* be run.
So you don't rule the full terminate in (iii) out? Apologies, I misunderstood that!
The final case is a process that is stuck in termination, presumably due to a bug in an unwind. This is the only point where we have the choice between ii and i.
So IMO the choice should be between terminate (ii) and terminateUnsafely (i). [I believe that in VisualWorks I called (i) terminateWithExtremePrejudice, after Apocalypse Now]. When a process starts to terminate, it is marked somehow as being terminated, running unwind blocks. If the normal terminate is sent to the process while in this state that terminate send should raise an error; attempting to terminate a process twice is an error. We cannot automatically decide if a process in this state is correctly running unwinds; that would be equivalent to solving the halting problem. Therefore we leave it up to the programmer to decide if, on discovering a terminating process stuck somewhere in running unwinds, they terminateUnsafely.
A key attitude here is to make the common case work well. Don't let the perfect be the enemy of the good. In this case there is no perfect solution; arbitrary errors *can* occur, *potentially*, in termination, but in practice are extremely rare. Keeping the system simple, comprehensible and maintainable is a prime directive. If a programmer can, for example, use the debugger to manually run undins that have not yet run because of a stuck unwind, then there is no absolute need for the system to handle this situation gracefully. A developer that gets themselves in hot water creating errors iu unwinds can get themselves out using the debugger.
So for me, KISS. Just support something that guards against multiple termination, and in the debugger, reveals clearly where a process is in the course of running unwinds.
As promised I've opened an issue for multiple termination or resuming a process while it's terminating: see http://lists.squeakfoundation.org/pipermail/squeak-dev/2021-December/217695.... and sent a proposition to the Inbox (Kernel-jar.1435, KernelTests-jar.417). To keep it simple I tried to avoid introducing a new state marking a process that starts terminating, and nilled the suspendedContext during the whole termination instead - in this case an attempt to resume terminating process will rais an error and an attempt to terminate a terminating process will raise an error too. To make it work I had to avoid using suspendedContext instance variable in your #releaseCriticalSection: and I moved it under Context because it needs to return now - just a suggestion... open to any criticism :)
And I really *don'* like the idea of running terminate in a separate process. This has bad performance implications. Most terminations can be done in the process itself; that should be the expectation.
I definitely lack experience to comment that... by many orders of magnitude :)
Thanks again for your comments; I look forward to any further remarks!
best,
~~~ ^[^ Jaromir
Sent from Squeak Inbox Talk
On 2021-12-16T14:20:01-08:00, eliot.miranda@gmail.com wrote:
Hi Jaromir,
I'm responding to this message because you introduce useful
categorisation of different kinds of terminate. I'll respond in context below.
On Mon, Nov 15, 2021 at 9:41 AM <mail at jaromir.net> wrote:
Hi Christoph,
Once again, apologies for the huge delay in my responding :)
Hm, I would rather say that closing the window should have the same
effect as pressing "abandon" - personally, I am very much used to this and we do not really have good options to communicate a different behavior of the close button.
Yes, you're right indeed.
A kill item in the stack trace menu sounds fine for now (even though
killing is not actually related to the selected context). In the long term, I would like to introduce menu buttons in the fashion of [1] in the Morphic UI.
Your Abandon button with options is terrific, please disregard my suggestion :)
look at something in the debugger (on a healthy process) and close the
window (i.e. full termination is appropriate and I'd even say preferable).
Can you help me please, if the process is healthy (so no unwind context
is lying on the stack), would there be any difference between the different terminate modi (ii) and (iii)?
I thought there was a difference, e.g. if you debug #ensure on a healthy process and want to abandon the debugger in the middle - does it make sense?
Example: [^2] ensure: [Transcript cr; show: 'done'. "do some cleaning"]
Now, you start a debugger, do a few steps and in the middle of the ensure block you decide you've seen enough and hit Abandon - if it's implemented with (ii), i.e. the 'aggressive', less safe version of terminate, the block won't get completed. If it contained some cleaning procedures, the cleaning wouldn't be executed... That's why I'm still leaning slightly towards full termination as a default Abandon action - but e.g. Visual Works use the less safe termination in this case, as you suggest. Other Smalltalks I checked (Cuis, Pharo and VA) don't have three ways to terminate at all (yet) :) However, ultimately users that use Squeak the most should have the final say. I'll be happy with both options. (Or make it a preference?).
[[x1 := nil] ensure: [x1 := (2 / 0)]] ensure: [self inform: x1 asString].
Hmm, this example of your nicely justifies your approach: Abandon = (ii) :D
Naming:
Hm, "light" reminds me of "friendly" and (iii) would certainly be more
friendly (giving the process more freedom for cleaning things up). Difficult ...
What about #terminateNow for (ii) and #terminate for (iii), slightly
related to #updateStyle and #updateStyleNow? Since (iii) might continue with the execution of the current halfway-executed context, (ii) is more urgent.
Another option would be "terminate: aggressive", analogously to
"cleanup: aggressive" on Behavior where #terminate would fall back to "terminate: false". But this would make the new mode a little bit worse accessible.
Agreed, "light" is not good. Checked Visual Works again and they use terminate for (iii), terminateUnsafely for (ii) and terminateUnsafelyNow for kill (i). #teminateNow suggests it would terminate now as opposed to later :) No hint about the nature of the procedure that will attempt to terminate with increased level of possible damage to the environment... But we're getting closer and again, I'll be happy with either of them :)
Here are some ideas for better names:
(i) #destroy (as opposed to #kill, this sounds more like the cheap and
unreactable operation it is)
(ii) #terminateAggressively, which redirects to #terminate: true (iii) #terminate, which redirects to #terminate: false
One more naming suggestion: (iii) terminate (~run all unwind blocks) (ii) terminateLessSafely (~skip unwind blocks in progress, run all not-yet-started ones) (i) terminateUnsafely (~kill, run no unwinds)
OK, this is really useful. It provides the straight-forward options we have for process termination. Now let's try and be categorical about unwind blocks.
Unwind blocks are used to maintain invariants. The classic example is Semaphore>>critical:
critical: mutuallyExcludedBlock "Evaluate mutuallyExcludedBlock only if the receiver is not currently in the process of running the critical: message. If the receiver is, evaluate mutuallyExcludedBlock after the other critical: message is finished." <criticalSection> self wait. ^mutuallyExcludedBlock ensure: [self signal]
Given that unwind blocks are used to maintain invariants, any terminate which does (iii), run all unwind blocks, including those that have already been run, is completely broken. We can therefore ignore (i). It would run unwinds more than once. The correct terminate is (ii). Skip unwind blocks already in progress. Presumably something very wrong has happened in an unwind block that has not completed. But all unwinds further away from top of stack should and must be run normally. Note that in the normal case there will be no such partial unwinds; it will not occur in correct code. Note further that its extremely unlikely that there will be an unwind within an unwind.
But there's an assumption here. In normal termination, unwind blocks will just run, and the process will terminate cleanly. If it terminates itself then there is no issue other than potential bugs in unwinds (likely during development, rather than normal running). An application that wants to terminate other processes should be written to terminate those processes it needs to only when those processes are at some safe point. I would not spend effort trying to make terminate safe when sent from other processes at arbitrary points. That counts as trying to shoot oneself in the foot.
So terminate boils down to two or three cases, depending on how you look at it. The first case, or pair of cases, is a process that terminates itself or is terminated by another process while at a safe point. In this case any and all unwinds *must* be run. The final case is a process that is stuck in termination, presumably due to a bug in an unwind. This is the only point where we have the choice between ii and i.
So IMO the choice should be between terminate (ii) and terminateUnsafely (i). [I believe that in VisualWorks I called (i) terminateWithExtremePrejudice, after Apocalypse Now]. When a process starts to terminate, it is marked somehow as being terminated, running unwind blocks. If the normal terminate is sent to the process while in this state that terminate send should raise an error; attempting to terminate a process twice is an error. We cannot automatically decide if a process in this state is correctly running unwinds; that would be equivalent to solving the halting problem. Therefore we leave it up to the programmer to decide if, on discovering a terminating process stuck somewhere in running unwinds, they terminateUnsafely.
A key attitude here is to make the common case work well. Don't let the perfect be the enemy of the good. In this case there is no perfect solution; arbitrary errors *can* occur, *potentially*, in termination, but in practice are extremely rare. Keeping the system simple, comprehensible and maintainable is a prime directive. If a programmer can, for example, use the debugger to manually run undins that have not yet run because of a stuck unwind, then there is no absolute need for the system to handle this situation gracefully. A developer that gets themselves in hot water creating errors iu unwinds can get themselves out using the debugger.
So for me, KISS. Just support something that guards against multiple termination, and in the debugger, reveals clearly where a process is in the course of running unwinds.
And I really *don'* like the idea of running terminate in a separate process. This has bad performance implications. Most terminations can be done in the process itself; that should be the expectation.
Marcel wrote:
How would you summarize the differences between #terminate,
#terminateAggressively, and #kill, each with 1-2 sentences? :-)
Inspired by VW:
(iii) "Terminate the process and allow all unwind blocks to run, even if they are currently in progress." This is the behavior we expect when terminating a healthy process.
(ii) "Terminate the process and run all not-yet-started unwind blocks; if the process is in the middle of an unwind block, then that unwind block will not be completed, but subsequent unwind blocks will be run." This is the behavior we expect when we close the debugger in a situation when the unwind block may not be able to complete, either because of an error, or because it is hung. In most other circumstances, we would want to use #terminate, which allows unwind blocks in progress to complete.
(i) "Terminate the process without attempting to run any of its unwind blocks." This is the behavior we might need in very rare circumstances when modes (iii) and (ii) fail.
Changeset:
Please find the attached changeset in which I have changed the names
accordingly and also have removed the kill/destroy operation from the UI as per request of Marcel. I aggree with his argument that destroy is a kind of private operation that affects the interna of the process and should be only used for troubleshooting, so we should not give this hack too much visibility.
Nice! I liked your Abandon button with options more but this is a start. Kill option: do you mean no access to the kill option from the Process browser either? Process browser is a very troubleshooting tool :)
And finally, If I may - I'd slightly prefer not adding another #terminate: to the Process class only to serve a single sender but rather inline it and address the two termination modes directly in the ProcessBrowser class:
ProcessBrowser class >> #terminateProcess: aProcess aggressively: aggressive
aProcess ifNil: [^ self]. self suspendedProcesses removeKey: aProcess ifAbsent: [].
aProcess terminate: aggressive.
aggressive ifFalse: [aProcess terminate] ifTrue: [aProcess terminateAggressively]
Concerning your #unwindTo: issue - not sure if this actually related to
this thread? Actually I've lost the overview which terminate mode - (ii) or (iii) - you are referring to, maybe we should discuss this in a separate conversation. :-)
Yes, it's a bug and deserves its own thread.
=== Summary:
TLDR: Concerning our next steps - consider this a small to-do list:
- [ ] We want to implement three modi of process termination:
- [x] (i) is not yet implemented but should be easy to implement
- [x] (ii) is in my proposal from [2], maybe needs further refinement
- [ ] (iii) is pretty much what we have in Process >> #terminate in
the current Trunk. Is there anything still missing yet?
- [ ] Decide on names for (ii) and (iii) - see above
- [ ] Decide on UI integration into debugger - see above
Add this one?
- [ ] Decide on UI integration into Process Browser
Regarding Process >> #terminate - the current Trunk version is not final; the final version is the Inbox's Kernel-jar.1414.
More or less done! :)
Thanks,
Jaromir
On 2021-08-30T12:51:21+02:00, christoph.thiede at student.hpi.uni-potsdam.de wrote:
Hi Marcel, hi all,
How would you summarize the differences between #terminate,
#terminateAggressively, and #kill, each with 1-2 sentences? :-)
Let's keep using the numbers from above instead, as the names are only
my imperfect proposals:
(i) run no unwind contexts (harshest possible way; currently only
achievable by doing "suspendedContext privSender: nil" prior to terminating). You would need this very rarely, if at all, for troubleshooting.
(ii) run not-yet started unwind contexts (this is what I proposed in
fix-Process-terminate.1.cs [1]). This is the behavior we expect from the Abandon button in a debugger. I think?
(iii) run all unwind contexts, including those that already have been
started (this is the most friendly way that you implemented in #terminate recently). This is the behavior we expect when terminating a healthy process.
Here are some ideas for better names:
(i) #destroy (as opposed to #kill, this sounds more like the cheap and
unreactable operation it is)
(ii) #terminateAggressively, which redirects to #terminate: true (iii) #terminate, which redirects to #terminate: false
Please find the attached changeset in which I have changed the names
accordingly and also have removed the kill/destroy operation from the UI as per request of Marcel. I aggree with his argument that destroy is a kind of private operation that affects the interna of the process and should be only used for troubleshooting, so we should not give this hack too much visibility.
Best, Christoph
Sent from Squeak Inbox Talk
On 2021-08-23T13:59:04+02:00, marcel.taeumel at hpi.de wrote:
Hi Christoph --
How would you summarize the differences between #terminate,
#terminateAggressively, and #kill, each with 1-2 sentences? :-)
Best, Marcel Am 23.08.2021 13:18:40 schrieb christoph.thiede at
student.hpi.uni-potsdam.de <christoph.thiede at student.hpi.uni-potsdam.de
:
Hi all, hi Jaromir,
based on the to-do list from my previous message, I have created a
changeset that adds Process >> #terminateAggressively (ii) and Process >> #kill (i) and integrates them into the process browser and the debugger. Most importantly, the following snippet will inform "nil" now instead of "ZeroDevision" after abandoning the error:
����[[x1 := nil] ensure: [x1 := (2 / 0)]] ��������ensure: [self inform: x1 asString].
With this changeset, I would be fully happy again with the
functionality of the debugger. :-) What remains to do is the following:
- [ ] We want to implement three modi of process termination:
- [x] (i) - see #kill
- [X] (ii) - see #terminateAggressively
- [ ] (iii) is pretty much what we have in Process >> #terminate in
the current Trunk. Is there anything still missing yet?
- [ ] Decide on names for (ii) and (iii) - see my previous message
- [ ] Decide on UI integration into debugger - proposal exists in
Process-terminateAggressively.1, but open for criticism
- [ ] Test #terminateAggressively (actualy, I don't have a good of
overview of all your new #terminate tests - probably it would be easiest to wait until they have arrived in the Trunk and then create copies of them to test #terminateAggressively analogously :D)
Best, Christoph
Sent from Squeak Inbox Talk [
https://github.com/hpi-swa-lab/squeak-inbox-talk]
On 2021-08-22T16:07:54+02:00, christoph.thiede at
student.hpi.uni-potsdam.de wrote:
Hi Jaromir,
back from vacation, *finally* I have found some time to return back
to this pleasant problem! :-)
Great, it sounds like we're already agreeing pretty much, just let
me address some small points:
Debugger - Abandon could be the lightweight version you proposed.
Why not
have a proper Abandon button for it? ����The right click menu on a context could offer the Kill option
(next to
'peel to first like this'); no button necessary. Now the question is what should be under the "window close"
red-circle-x -
heavyweight terminate? I'm thinking this scenario: if the debugger
returns
after closing the window you start thinking what happened and use
Abandon;
if it still doesn't help you go right-click and kill it?
My usual scenario is (limited experience however): look at
something in the
debugger (on a healthy process) and close the window (i.e. full
termination
is appropriate and I'd even say preferable). If something goes
wrong - then
I'd welcome a hint there are options - thus the proper Abandon
button - what
do you think?
Hm, I would rather say that closing the window should have the same
effect as pressing "abandon" - personally, I am very much used to this and we do not really have good options to communicate a different behavior of the close button. I think presenting the new behaviors Kill (i) and (iii) in some kind of menu would be less confusing.
A kill item in the stack trace menu sounds fine for now (even though
killing is not actually related to the selected context). In the long term, I would like to introduce menu buttons in the fashion of [1] in the Morphic UI (Marcel is currently reviewing my proposal for this), then we could move Kill into the Abandon menu, but for now, your proposal sounds perfectly fine to me. :-)
look at something in the debugger (on a healthy process) and close
the window (i.e. full termination is appropriate and I'd even say preferable).
Can you help me please, if the process is healthy (so no unwind
context is lying on the stack), would there be any difference between the different terminate modi (ii) and (iii)?
> But I don't have any good idea for version (ii) yet. Call it
#abandon like
> in the debugger? Then again, #abandon is rather a verb from the
Morphic
> language. Further possible vocables (according to my synonym
thesaurus)
> include #end, #stop, #finish, #unwind, #abort, #exit. Please
help... :-)
I'd probably go with something like #terminateLight because it's a
proper
process termination including unwinds except the ones currently in
progress
- so it is a light version of #terminate :) I've checked
VisualWorks: they
chose #terminateUnsafely for this type of termination which I
don't like
much, it sounds too negative; the real meaning is rather #terminateAsSafelyAsPossibleGivenTheCircumstances ;).
Hm, "light" reminds me of "friendly" and (iii) would certainly be
more friendly (giving the process more freedom for cleaning things up). Difficult ...
What about #terminateNow for (ii) and #terminate for (iii), slightly
related to #updateStyle and #updateStyleNow? Since (iii) might continue with the execution of the current halfway-executed context, (ii) is more urgent.
Another option would be "terminate: aggressive", analogously to
"cleanup: aggressive" on Behavior where #terminate would fall back to "terminate: false". But this would make the new mode a little bit worse accessible.
Concerning your #unwindTo: issue - not sure if this actually related
to this thread? Actually I've lost the overview which terminate mode - (ii) or (iii) - you are referring to, maybe we should discuss this in a separate conversation. :-)
TLDR: Concerning our next steps - consider this a small to-do list:
- [ ] We want to implement three modi of process termination:
����- [ ] (i) is not yet implemented but should be easy to implement ����- [ ] (ii) is in my proposal from [2], maybe needs further
refinement
����- [ ] (iii) is pretty much what we have in Process >> #terminate
in the current Trunk. Is there anything still missing yet?
- [ ] Decide on names for (ii) and (iii) - see above
- [ ] Decide on UI integration into debugger - see above
Further answers following soon.
Best, Christoph
[1]
https://user-images.githubusercontent.com/48567372/60924070-4472fe80-a26e-11...
[2]
http://lists.squeakfoundation.org/pipermail/squeak-dev/2021-May/215608.html (fix-Process-terminate.cs)
Sent from Squeak Inbox Talk
On 2021-05-29T14:31:13-05:00, m at jaromir.net wrote:
Hi Christoph,
> Jaromir, your proposal to provide multiple selectors for
modeling separate
> modes of termination sounds like a very good idea to me. But how
many
> different modes do we actually need? So far I can count three
modes:
> > (i) run no unwind contexts (harshest possible way; currently only > achievable by doing "suspendedContext privSender: nil" prior to > terminating) > (ii) run not-yet started unwind contexts (this is what I
proposed in
> fix-Process-terminate.1.cs [1]) > (iii) run all unwind contexts, including those that already have
been
> started (this is the most friendly way that you implemented in
#terminate
> recently)
I think this is it.
Litereally minutes ago had to use privSender: nil to get rid of a
debugger
:) Fully terminate really is too strong to recover from fatal
errors.
> ... my point here is: Proceeding from an error almost always
doesn't seem
> "right". :-) It is always a decision by the debugging programmer
to
> override the default control flow and switch to the "next
plausible
> alternative control flow", i.e., resume as if the error would
have never
> been raised.
yes - I'd add: even an error may quite often be completely benign,
like
'Transcript show: 1/0' - possibly a typo so you just may want to
Proceed or
fully terminate. In case the error damages a whole subsequent
chain of
events, you're absolutely right a full termination seems a silly
option and
a light version of terminate may be the most appropriate.
So I fully agree the decision which termination mode it is stays
with the
user - so I'm all for giving the user the choices you suggested.
> \1. Which mode should we use in which situations? > > I think this debate could benefit from a few more concrete usage > scenarios. I'm just collecting some here (thinking aloud): > > - Process Browser: We can provide multiple options in the
process menu.
> - Debugger: I agree with you that Abandon should always run
not-yet
> started unwind contexts but never resume halfway-executed unwind
contexts.
> So this maps to to mode (ii) from above. > - Skimming through most senders of #terminate in the image,
they often
> orchestrate helper processes, deal with unhandled errors or
timeouts, or
> do similar stuff - usually they should be very fine with the
friendly
> version of #terminate, i.e. mode (iii) from above. I think. > - Regarding option (1), I think you would need it extremely
seldom but
> maybe in situations like when your stack contains a loop, your
unwind
> contexts will cause a recursion/new error, or you deliberately
want to
> prevent any unwind context from running. No objections against
adding a
> small but decent button for this in the debugger. :-) > > Would you agree with these behaviors? Maybe you can add further
examples
> to the list?
Yes
Process Browser - the right click menu could provide all options
Debugger - Abandon could be the lightweight version you proposed.
Why not
have a proper Abandon button for it? ����The right click menu on a context could offer the Kill option
(next to
'peel to first like this'); no button necessary. ����Now the question is what should be under the "window close"
red-circle-x -
heavyweight terminate? I'm thinking this scenario: if the debugger
returns
after closing the window you start thinking what happened and use
Abandon;
if it still doesn't help you go right-click and kill it?
My usual scenario is (limited experience however): look at
something in the
debugger (on a healthy process) and close the window (i.e. full
termination
is appropriate and I'd even say preferable). If something goes
wrong - then
I'd welcome a hint there are options - thus the proper Abandon
button - what
do you think?
> \2. How should we name them? > > Direct proposal: (i) #kill and (iii) #terminate. > After looking up the original behavior of #terminate in Squeak
5.3, I
> think it would be consistent to resume all halfway-executed
unwind
> contexts in this method. So yes, I also withdraw my criticism
about
> #testNestedUnwind. :-) > > But I don't have any good idea for version (ii) yet. Call it
#abandon like
> in the debugger? Then again, #abandon is rather a verb from the
Morphic
> language. Further possible vocables (according to my synonym
thesaurus)
> include #end, #stop, #finish, #unwind, #abort, #exit. Please
help... :-)
I'd probably go with something like #terminateLight because it's a
proper
process termination including unwinds except the ones currently in
progress
- so it is a light version of #terminate :) I've checked
VisualWorks: they
chose #terminateUnsafely for this type of termination which I
don't like
much, it sounds too negative; the real meaning is rather #terminateAsSafelyAsPossibleGivenTheCircumstances ;).
I'm wondering whether #unwindTo: (used ony by Generator) is bugged
(with
regard to dealing with non-local returns), and could be
fixed/unified with
your approach. Look at these examples:
p := [[Processor activeProcess suspend] valueUninterruptably] fork. Processor yield. p suspendedContext unwindTo: nil
or
p := [[:exit | [Processor activeProcess suspend] ensure: [exit
value]]
valueWithExit] fork. Processor yield. p suspendedContext unwindTo: nil
If you do `p terminate` instead of `p suspendedContext unwindTo:
nil`, it
works fine, but #unwindTo causes a block cannot return error - I
think it's
the same bug all over again :) #value evaluates the non-local
return on the
wrong stack...
Regarding our cannot return discussion - I have to think about it
and I'll
post my reply later in [1] to keep it separate :)
Thanks again and regards,
[1]
http://forum.world.st/The-Inbox-Kernel-ct-1405-mcz-td5129706.html#a5130114
^[^ Jaromir
Sent from: http://forum.world.st/Squeak-Dev-f45488.html
squeak-dev@lists.squeakfoundation.org