[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