[squeak-dev] The Trunk: Kernel-mt.1479.mcz

commits at source.squeak.org commits at source.squeak.org
Thu Jun 9 08:15:01 UTC 2022


Marcel Taeumel uploaded a new version of Kernel to project The Trunk:
http://source.squeak.org/trunk/Kernel-mt.1479.mcz

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

Name: Kernel-mt.1479
Author: mt
Time: 9 June 2022, 10:14:59.58973 am
UUID: 080c46b2-93b7-6040-8181-98ab2dcc8df7
Ancestors: Kernel-ct.1473, Kernel-jar.1478

Merges Kernel-jar.1478 (starting at jar.1473).

See commentary in those other versions' messages, which I will upload soon.

=============== Diff against Kernel-ct.1473 ===============

Item was changed:
  ----- Method: Context>>runUntilReturnFrom: (in category 'private-exceptions') -----
  runUntilReturnFrom: aContext
  	"Run the receiver (which must be its stack top context) until aContext returns. Avoid a context that cannot return.
  	 Note: to avoid infinite recursion of MNU error inside unwind blocks, implement e.g. a wrapper around the message
  	 sentTo: receiver in #doesNotUnderstand:. Note: This method is a trivialized version of #runUntilErrorOrReturnFrom:
  	 and was intended to be used by #unwindTo as a helper method to unwind non-local returns inside unwind blocks."
  
  	| here unwindBottom newTop |
  	here := thisContext.
+ 	"Avoid a context that cannot return between self and aContext (see Note 1 below)."
+ 	unwindBottom := self findContextSuchThat: [:ctx | ctx == aContext or: [ctx selector = #cannotReturn:]].
+ 	newTop := unwindBottom sender.
- 	"Avoid a context that cannot return (see Note 1 below)"
- 	unwindBottom := (self findContextSuchThat: [:ctx | ctx selector = #cannotReturn:]) ifNil: [aContext].
- 	newTop := aContext sender.
  	"Insert ensure context under unwindBottom in self's stack (see Note 2 below)"
  	unwindBottom insertSender: (Context contextEnsure: [here jump]).
  	self jump.  "Control jumps to the receiver's stack (see Note 2 below)"
  	"Control resumes here once the above inserted ensure block is executed (see #jump comments)"
  	^newTop  "Return the new top context (see Note 3 below)"
  
  	"Note 1: returning from #cannotReturn's sender would crash the VM so we install a guard ensure context right
  	 above it; after returning here the unwind will continue safely. Try running and debugging this example
  	 (avoid Proceeding the BCR error though; it may indeed crash the image):
  	 [[[] ensure: [^2]] ensure: [^42]] fork"
  
  	"Note 2: the receiver (self) is run by jumping directly to it (the active process abandons thisContext and executes
  	 self on its own stack; self must be its top context). However, before jumping to self we insert an ensure block under
  	 unwindBottom context that will execute a jump back to thisContext when evaluated. The inserted guard ensure
  	 context is removed once control jumps back to thisContext."
  
  	"Note 3: it doesn't matter newTop is not a proper stack top context because #unwindTo will only use it as a starting
  	 point in the search for the next unwind context and the computation will never return here. We could make newTop
  	 a proper top context by pushing nil to its stack (^newTop push: nil) if need be (see #jump comments).
  	 Cf. the pattern in #runUntilErrorOrReturnFrom:: removing the inserted ensure context by stepping until popped
  	 when executing non-local returns wouldn't work here and would fail tests testTerminateInNestedEnsureWithReturn1
  	 through 4."!

Item was changed:
  ----- Method: Process class>>forContext:priority: (in category 'instance creation') -----
  forContext: aContext priority: anInteger 
+ 	"Answer an instance of me that has suspended aContext at priority anInteger. 
+ 	It's important to set the priority before setting the suspendedContext; otherwise
+ 	an endless stream of error windows will start poping up when debugging e.g.
+ 		[] newProcess
+ 	when Process Browser is open with auto-update on."
- 	"Answer an instance of me that has suspended aContext at priority 
- 	anInteger."
  
  	| newProcess |
  	newProcess := self new.
- 	newProcess suspendedContext: aContext asContext.
  	newProcess priority: anInteger.
+ 	newProcess suspendedContext: aContext asContext.
  	^newProcess!

Item was changed:
  ----- Method: Process>>suspendedContext: (in category 'private') -----
+ suspendedContext: aContextOrNil
+ 	"Set suspendedContext; proceed with caution when assigning nil or when process priority is undefined."
+ 	
+ 	"Note: Assigning nil to a runnable but not active process would freeze the image when self is scheduled to run.
+ 	 
+ 	 Workspace example:
+ 	 These two lines (executed at once, not line by line)
+ 		p := [] newProcess resume.
+ 		p suspendedContext: nil
+ 	 will freeze the image; the first line puts p in the run queue, the second line niles p's suspendedContext and
+ 	 when the UI cycles, p tries to run with niled suspendedContext and the image freezes as a result.
+ 	
+ 	 Assigning 'suspendedContext' before 'priority' is defined may cause a disaster when Process Browser is open  
+ 	 with auto-update on; once the 'suspendedContext' is set, the new process is no longer considered terminated
+ 	 and Process Browser will try to place it in its list of processes but encounters a nil error when reading its priority
+ 	 because it has not been set yet.
+ 	 
+ 	 Workspace example:
+ 	 If you run the following line with Process Browser open and auto-update on, you'll ruin your image:
+ 		p := Process new suspendedContext: [self] asContext
+ 	 Every second a new debugger window pops up and the only way out is to kill the image in the OS.
+ 	
+ 	 As a precautionary measure set 'priority' if undefined to the active process priority before setting
+ 	 'suspendedContext'."
- suspendedContext: aContext
  
+ 	priority ifNil: [priority := Processor activePriority].
+ 	suspendedContext := aContextOrNil ifNil: [self suspend. nil]!
- 	suspendedContext := aContext!

Item was changed:
  ----- Method: Process>>terminate (in category 'changing process state') -----
  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."
- 	 allow all unwind blocks to run; if they are currently in progress, let them finish.
- 	 If the process is in the middle of a #critical: critical section, release it properly."
  	
+ 	 "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.
- 	 with process termination when closing the debugger or after a catastrophic failure."
  	
+ 	 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 terminating the active process, create a parallel stack and run unwinds from there;
- 	 if terminating a suspended process, again, create a parallel stack for the process being
- 	 terminated and resume the suspended process to complete its termination from the new
- 	 parallel stack. Use a priority higher than the active priority to make the process that
- 	 invoked the termination wait for its completion."
  
+ 	 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."
+ 	
- 	"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 pre-2022 #suspend simply removed the process from
- 	 the conditional variable's list it was previously waiting on; see Process>>suspend comments.
- 	 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."
- 
  	| context |
  	self isActiveProcess ifTrue: [
  		context := thisContext.
+ 		^[[] ensure: [context unwindTo: nil]. self suspend] asContext jump].
- 		^[context unwindTo: nil. self suspend] asContext jump].
  
+ 	[] ensure: [ | terminator |
- 	[] ensure: [ 
  		self suspendAndReleaseCriticalSection.
  		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]!
- 		suspendedContext := [context unwindTo: nil. self suspend] asContext.
- 		self priority: (Processor activePriority + 1 min: Processor highestPriority); resume]!



More information about the Squeak-dev mailing list