[Pkg] 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 Packages
mailing list