[squeak-dev] The Inbox: Kernel-jar.1487.mcz

commits at source.squeak.org commits at source.squeak.org
Mon Jul 11 13:00:37 UTC 2022

A new version of Kernel was added to project The Inbox:

==================== Summary ====================

Name: Kernel-jar.1487
Author: jar
Time: 11 July 2022, 3:00:32.776972 pm
UUID: 729cdb60-4feb-0a42-b966-3cb2f9b5c338
Ancestors: Kernel-eem.1486

Add a stronger guard to deal with concurrent terminations.

The current weakness showed in this Workspace example:

p1 := [Smalltalk installLowSpaceWatcher] fork.
p2 := [Smalltalk installLowSpaceWatcher] fork

Instead of terminating both LowSpaceProcesses and unblocking p1 and p2, one of them gets stuck on a semaphore.

Complemented with KernelTests-jar.437 with a modified test (testTerminateTerminatingProcessInUnwindTo) to cover this situation.

=============== Diff against Kernel-eem.1486 ===============

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."
+ 	^(Context contextEnsure: [self unwindTo: nil]) 
+ 		privSender: [aProcess suspend] asContext
+ !

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;
  	 allow all unwind blocks to run; if they are currently in progress, let them finish."
  	 "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. 
  	 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 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
  	 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: [ | terminator |
  		self suspendAndReleaseCriticalSection.
+ 		suspendedContext ifNil: [^self].
- 		context := suspendedContext ifNil: [^self].
  		terminator := Semaphore new.
+ 		suspendedContext bottomContext insertSender: (Context contextEnsure: [terminator signal]).
+ 		self suspendedContext: (suspendedContext unwindAndStop: self);
- 		context bottomContext insertSender: (Context contextEnsure: [terminator signal]).
- 		self suspendedContext: [[] ensure: [context unwindTo: nil]. self suspend] asContext;
  			priority: Processor activePriority;
  		terminator wait]!

More information about the Squeak-dev mailing list