A new version of Kernel was added to project The Inbox: http://source.squeak.org/inbox/Kernel-jar.1554.mcz
==================== Summary ====================
Name: Kernel-jar.1554 Author: jar Time: 23 January 2024, 3:43:30.880479 pm UUID: 7ceb0fd5-d407-b747-a8fd-7549b90acaff Ancestors: Kernel-mt.1551
fix a deficiency in the unwind procedure; the bug manifested in Christoph's heavily multithreded environment in the Simulation Studio.
the exact mechanism of the bug: when a process is in the middle of an unwind (during e.g. exception resume etc) and is terminated, i.e. another unwind invoked, at a certain instruction the termination fails to unwind correctly.
Currently, the unwind block is mark as completed before the execution of the block starts (see #ensure: or #unwindTo:safely: - the complete variable is set to true). The fix is to add another mark to indicate the unwind block execution has really completed (for simplicity this is achieved by setting the complete variable to false and this mark is used at #unwindTo:safely: to distinguish not yet completed unwind blocks and the really completed ones).
If this proves a good fix the name of the `complete` variable defined in #ensure: may be changed to better correspond to this extended "semantics" of the variable :)
=============== Diff against Kernel-mt.1551 ===============
Item was changed: ----- Method: Context>>unwindTo:safely: (in category 'private-exceptions') ----- unwindTo: aContext safely: aBoolean "Unwind self to aContext to execute pending #ensure:/#ifCurtailed: argument blocks between self and aContext. If aBoolean is false, unwind only blocks that have not run yet, otherwise complete all pending unwind blocks including those currently in the middle of their execution; these blocks will just finish their execution. Run all unwinds on their original stack using #runUntilReturnFrom:." | top ctx | ctx := top := self. aBoolean ifTrue: [ "If self is the top context of a stack already halfways through an unwind block, complete the outer-most unfinished unwind block first; all nested pending unwind blocks will be completed in the process; see testTerminationDuringUnwind and tests in ProcessTest/UnwindTest. Note: Halfway-through blocks have already set the complete variable (ctxt tempAt: 2) in their defining #ensure:/#ifCurtailed contexts from nil to true; we'll search for the bottom-most one." | outerMost | ctx isUnwindContext ifFalse: [ctx := ctx findNextUnwindContextUpTo: aContext]. [ctx isNil] whileFalse: [ + (ctx tempAt: 2) ifNotNil: [:notYetComplete | + notYetComplete ifTrue: [outerMost := ctx]]. - (ctx tempAt: 2) ifNotNil: [ - outerMost := ctx]. ctx := ctx findNextUnwindContextUpTo: aContext]. outerMost ifNotNil: [top := top runUntilReturnFrom: outerMost]]. "By now no halfway-through unwind blocks are on the stack. Note: top points to the former outerMost sender now, i.e. to the next context to be explored."
ctx := top ifNil: [^self]. "#findNextUnwindContextUpTo: starts searching from the receiver's sender so we must check the receiver explicitly whether it is an unwind context; see testTerminateEnsureAsStackTop. Create a new top context (i.e. a new branch off the original stack) for each pending unwind block (ctxt tempAt: 1) and execute it on the unwind block's stack to evaluate non-local returns correctly." ctx isUnwindContext ifFalse: [ctx := ctx findNextUnwindContextUpTo: aContext]. [ctx isNil] whileFalse: [ (ctx tempAt: 2) ifNil: [ ctx tempAt: 2 put: true. (ctx tempAt: 1) ifNotNil: [:unwindBlock | top := unwindBlock asContextWithSender: ctx. top runUntilReturnFrom: top]]. + ctx tempAt: 2 put: false. "mark the unwind block as completed; cf. notYetComplete temp above" ctx := ctx findNextUnwindContextUpTo: aContext] "Note: Cf. the unwind pattern in the previous versions of unwindTo: (1999-2021). Using #value instead of #runUntilReturnFrom: lead to a failure to evaluate some non-local returns correctly; a non-local return must be evaluated in the evaluation context (sender chain) in which it was defined."!
squeak-dev@lists.squeakfoundation.org