A new version of Kernel was added to project The Inbox:
==================== Summary ====================
Time: 31 March 2021, 11:11:51.005212 pm
Fixes current implementation ignoring some nested unwind blocks in common situations. Explained and documented in http://forum.world.st/Another-bug-in-Process-terminate-in-unwinding-contexts-tp5128171p5128178.html
A test follows.
=============== Diff against Kernel-mt.1383 ===============
Item was changed:
----- Method: Process>>terminate (in category 'changing process state') -----
"Stop the process that the receiver represents forever.
Unwind to execute pending ensure:/ifCurtailed: blocks before terminating.
If the process is in the middle of a critical: critical section, release it properly."
+ | ctxt unwindBlock oldList outerMost |
- | ctxt unwindBlock oldList |
self isActiveProcess ifTrue:
[ctxt := thisContext.
[ctxt := ctxt findNextUnwindContextUpTo: nil.
ctxt ~~ nil] whileTrue:
[(ctxt tempAt: 2) ifNil:
["N.B. Unlike Context>>unwindTo: we do not set complete (tempAt: 2) to true."
unwindBlock := ctxt tempAt: 1.
thisContext terminateTo: ctxt.
thisContext terminateTo: nil.
"If the process is resumed this will provoke a cannotReturn: error.
Would self debug: thisContext title: 'Resuming a terminated process' be better?"
"Always suspend the process first so it doesn't accidentally get woken up.
N.B. If oldList is a LinkedList then the process is runnable. If it is a Semaphore/Mutex et al
then the process is blocked, and if it is nil then the process is already suspended."
oldList := self suspend.
["Release any method marked with the <criticalSection> pragma.
The argument is whether the process is runnable."
self releaseCriticalSection: (oldList isNil or: [oldList class == LinkedList]).
+ "If terminating a process halfways through an unwind, try to complete that unwind block first;
+ if there are multiple such nested unwind blocks, try to complete the outer-most block
+ (i.e. the one closest to the bottom of the stack)"
+ ctxt := suspendedContext.
+ [(ctxt := ctxt findNextUnwindContextUpTo: nil) isNil] whileFalse:
+ "contexts under evaluation have already set their complete (tempAt: 2) to true"
+ [(ctxt tempAt:2) ifNotNil: [outerMost := ctxt]].
+ outerMost ifNotNil: [
+ "This is the outer-most unwind context currently under evaluation;
+ let's find an inner context executing outerMost's argument block (tempAt: 1)"
+ (suspendedContext findContextSuchThat: [:ctx |
+ ctx closure == (outerMost tempAt: 1)]) ifNotNil: [:inner |
+ suspendedContext runUntilErrorOrReturnFrom: inner.
+ "update receiver's suspendedContext reset in the previous step"
+ suspendedContext := outerMost]].
- "If terminating a process halfways through an unwind, try to complete that unwind block first."
- (suspendedContext findNextUnwindContextUpTo: nil) ifNotNil:
- (suspendedContext findContextSuchThat:[:c| c closure == (outer tempAt: 1)]) ifNotNil:
- [:inner| "This is an unwind block currently under evaluation"
- suspendedContext runUntilErrorOrReturnFrom: inner]].
ctxt := self popTo: suspendedContext bottomContext.
ctxt == suspendedContext bottomContext ifFalse:
[self debugWithTitle: 'Unwind error during termination' translated full: false].
"Set the context to its endPC for the benefit of isTerminated."
ctxt pc: ctxt endPC]!
|Free forum by Nabble||Edit this page|