[squeak-dev] The Inbox: Kernel-jar.1498.mcz
commits at source.squeak.org
commits at source.squeak.org
Mon Jan 30 16:33:19 UTC 2023
A new version of Kernel was added to project The Inbox:
http://source.squeak.org/inbox/Kernel-jar.1498.mcz
==================== Summary ====================
Name: Kernel-jar.1498
Author: jar
Time: 30 January 2023, 5:33:16.106677 pm
UUID: f76b2d65-c441-1a44-a925-e7c1e0d45cbb
Ancestors: Kernel-tpr.1497
fix four currently failing tests; improve unwind's behavior in #critical sections and #terminate resiliency in case of accidental preemption; fix a bug in #unwindAndStop:, add more tests and comments
complemented by KernelTests-jar.443
please kindly remove KernelTests-jar.437 and Kernel-jar.1487 from the Inbox.
=============== Diff against Kernel-tpr.1497 ===============
Item was added:
+ ----- Method: Context>>unwindAndStop: (in category 'private') -----
+ unwindAndStop: aProcess
+ "I'm a helper method to #terminate; I create and answer
+ a helper stack for a terminating process to unwind itself from.
+ Note: push a fake return value to create a proper top context."
+
+ ^(self class contextEnsure: [self unwindTo: nil])
+ privSender: [aProcess suspend] asContext;
+ push: nil
+ !
Item was changed:
----- Method: Process>>suspendAndReleaseCriticalSection (in category 'private') -----
suspendAndReleaseCriticalSection
"Figure out if we are terminating a process that is in the ensure: block of a critical section.
If it hasn't made progress but is beyond the wait (which we can tell by the oldList being
+ one of the runnable lists, i.e. a LinkedList, not a Semaphore or Mutex), then the ensure:
- one of the runnable lists, i.e. a LinkedList, not a Semaphore or Mutex, et al), then the ensure:
block needs to be run."
| oldList selectorJustSent |
+ "Suspend and unblock the receiver from a condition variable using the suspend primitive #88.
- "Suspend and unblock the receiver from a condition variable using suspend primitive #88.
It answers the list the receiver was on before the suspension."
oldList := self suspendAndUnblock.
+ suspendedContext ifNotNil: [:context |
+ (context method pragmaAt: #criticalSection) ifNil: [^self].
+ "If still blocked at the condition variable of a critical section, skip the rest of the current context."
+ (oldList isNil or: [oldList class == LinkedList]) ifFalse: [
+ suspendedContext := context pc: context endPC.
+ ^self].
+ "Now we are somewhere beyond the wait/lock primitive, figure out where, and if necessary,
+ step into the ensure block to allow the unwind block to be run."
+ selectorJustSent := context selectorJustSentOrSelf.
- (oldList isNil or: [oldList class == LinkedList]) ifFalse: [^self].
+ "If still haven't made progress beyond the wait then the ensure block has not been activated,
+ so step into it."
+ selectorJustSent == #wait ifTrue: [
+ suspendedContext := context stepToCallee].
- ((suspendedContext ifNil: [^self]) method pragmaAt: #criticalSection) ifNil: [^self].
- selectorJustSent := suspendedContext selectorJustSentOrSelf.
+ "If still haven't made progress beyond the lock primitive and the lock primitive just acquired ownership
+ (indicated by it answering false) then the ensure block has not been activated, so step into it."
+ (selectorJustSent == #primitiveEnterCriticalSection
+ or: [selectorJustSent == #primitiveTestAndSetOwnershipOfCriticalSection]) ifTrue: [
+ (context stackPtr > 0 and: [context top == false]) ifTrue: [
+ suspendedContext := context stepToCallee]]]!
- "If still at the wait the ensure: block has not been activated, so signal to restore."
- selectorJustSent == #wait ifTrue:
- [suspendedContext receiver signal].
-
- "If still at the lock primitive and the lock primitive just acquired ownership (indicated by it answering false)
- then the ensure block has not been activated, so explicitly primitiveExitCriticalSection to unlock."
- (selectorJustSent == #primitiveEnterCriticalSection
- or: [selectorJustSent == #primitiveTestAndSetOwnershipOfCriticalSection]) ifTrue:
- [(suspendedContext stackPtr > 0
- and: [suspendedContext top == false]) ifTrue:
- [suspendedContext receiver primitiveExitCriticalSection]]!
Item was changed:
----- Method: Process>>terminate (in category 'changing process state') -----
+ terminate
+ "Stop the process that the receiver represents forever. Allow all pending unwind
+ blocks to run before terminating; if they are currently in progress, let them finish."
- terminate
- "Stop the process that the receiver represents forever.
- Unwind to execute pending #ensure:/#ifCurtailed: blocks before terminating;
- allow all unwind blocks to run; if they are currently in progress, let them finish."
+ "Note: This is the kind of behavior we expect when terminating a healthy process.
- "This is the kind of behavior we expect when terminating a healthy process.
See further comments in #terminateAggressively and #destroy methods dealing
with process termination when closing the debugger or after a catastrophic failure.
If terminating the active process, create a new stack and run unwinds from there.
If terminating a suspended process (including runnable and blocked), always
+ suspend the terminating process first so it doesn't accidentally get woken up,
+ and nil the suspended context to prevent accidental resumption or termination
+ while manipulating the suspended context.
+
- suspend the terminating process first so it doesn't accidentally get woken up.
Equally important is the side effect of the suspension: In 2022 a new suspend
semantics has been introduced: the revised #suspend backs up a process waiting
on a conditional variable to the send that invoked the wait state, while the previous
#suspend simply removed the process from the conditional variable's list it was
previously waiting on; see #suspend and #suspendAndUnblock comments.
+
+ If the process is blocked, waiting to access the #critical: section, release it properly.
-
- If the process is in the middle of a #critical: critical section, release it properly.
To allow a suspended process to unwind itself, create a new stack for the process
being terminated and resume the suspended process to complete its termination
+ from the new, parallel stack. Use a semaphore to make the process that invoked
- from the new parallel stack. Use a semaphore to make the process that invoked
the termination wait for self's completion. Execute the termination in the ensure
argument block to ensure it completes even if the terminator process itself gets
terminated before it's finished; see testTerminateInTerminate and others."
- | context |
self isActiveProcess ifTrue: [
+ ^(thisContext unwindAndStop: self) jump].
- context := thisContext.
- ^[[] ensure: [context unwindTo: nil]. self suspend] asContext jump].
+ [] ensure: [
- [] ensure: [ | terminator |
self suspendAndReleaseCriticalSection.
+ suspendedContext ifNotNil: [:context | | terminator |
+ suspendedContext := nil.
+ terminator := Semaphore new.
+ context bottomContext insertSender: (Context contextEnsure: [terminator signal]).
+ self priority: Processor activePriority;
+ suspendedContext: (context unwindAndStop: self);
+ resume.
+ terminator wait]]!
- context := suspendedContext ifNil: [^self].
- terminator := Semaphore new.
- context bottomContext insertSender: (Context contextEnsure: [terminator signal]).
- self suspendedContext: [[] ensure: [context unwindTo: nil]. self suspend] asContext;
- priority: Processor activePriority;
- resume.
- terminator wait]!
More information about the Squeak-dev
mailing list
|