Marcel Taeumel uploaded a new version of KernelTests to project The Treated Inbox:
http://source.squeak.org/treated/KernelTests-jar.426.mcz
==================== Summary ====================
Name: KernelTests-jar.426
Author: jar
Time: 3 June 2022, 7:13:06.751641 pm
UUID: 60821aa1-f749-134e-922d-e9588194888c
Ancestors: KernelTests-mt.425
Test whether priority is set before suspendedContext during process creation.
Complements Kernel-jar.1473
Setting priority after causes an endless stream of error windows when debugging e.g.
[] newProcess
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."
=============== Diff against KernelTests-mt.425 ===============
Item was added:
+ ----- Method: ProcessTest>>testPrioritySetBeforeSuspendedContext (in category 'tests') -----
+ testPrioritySetBeforeSuspendedContext
+ "Test whether priority is set before suspendedContext during process creation."
+
+ "Setting priority after causes an endless stream of error windows when debugging e.g.
+ [] newProcess
+ 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."
+
+ | p inside |
+ inside := false.
+ p := [inside := true. [] newProcess] newProcess.
+ p runUntil: [:ctx | inside].
+ p runUntil: [:ctx | ctx selectorToSendOrSelf = #suspendedContext:].
+ self assert: (p suspendedContext tempAt: 3) priority notNil!
Marcel Taeumel uploaded a new version of Tests to project The Treated Inbox:
http://source.squeak.org/treated/Tests-jar.476.mcz
==================== Summary ====================
Name: Tests-jar.476
Author: jar
Time: 29 January 2022, 8:45:33.967173 pm
UUID: baedfdf3-299a-bf46-8ab5-61057c90b99c
Ancestors: Tests-ct.475
fix an error in my previous contribution; I used a class instead of an instance of an exception as an argument to #resignalAs:
The test worked because current implementation of #resignalAs allows it but using a class instead of an instance is not consistent with ANSI specification.
=============== Diff against Tests-ct.475 ===============
Item was changed:
----- Method: ExceptionTester>>doubleOuterResignalAsTest (in category 'tests') -----
doubleOuterResignalAsTest
"ExceptionTester new doubleOuterResignalAsTest"
[[self doSomething.
MyResumableTestError signal.
self doYetAnotherThing]
on: MyResumableTestError
do: [:ex | ex outer. self doSomethingExceptional].
self doSomethingElse]
on: MyResumableTestError
+ do: [:ex | ex resignalAs: MyTestNotification new]
- do: [:ex | ex resignalAs: MyTestNotification]
!
Marcel Taeumel uploaded a new version of Kernel to project The Treated Inbox:
http://source.squeak.org/treated/Kernel-jar.1478.mcz
==================== Summary ====================
Name: Kernel-jar.1478
Author: jar
Time: 8 June 2022, 11:16:07.949882 pm
UUID: f5720d99-9ce2-374c-adb0-811c23b2cfe5
Ancestors: Kernel-jar.1477
Fix a bug in #runUntilReturnFrom:: while searching for a context that cannot return (to avoid the VM crash) I forgot to limit the search to the relevant part of the stack only (i.e. search only *between* the contexts to be executed). As a result the following example currently fails to unwind:
p := [[[^2] on: BlockCannotReturn do: [Semaphore new wait]] ensure: [Transcript show: 'been here ']] fork.
Processor yield.
[p terminate] fork
Plus a minor change to allow unwinding even in this case:
p := [[] ensure: [[[^2] on: BlockCannotReturn do: [Semaphore new wait]] ensure: [Transcript show: 'been here ']]] fork.
Processor yield.
[p terminate] fork
A test will follow later.
=============== Diff against Kernel-jar.1477 ===============
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."!
Marcel Taeumel uploaded a new version of Kernel to project The Treated Inbox:
http://source.squeak.org/treated/Kernel-jar.1477.mcz
==================== Summary ====================
Name: Kernel-jar.1477
Author: jar
Time: 8 June 2022, 1:18:13.217988 pm
UUID: cde25b8e-8f6a-4241-8f6e-67a9ae27da68
Ancestors: Kernel-jar.1476
Setting suspendedContext may ruin your image; add checks to prevent such situations. Add examples and coments documenting disastrous situations.
Supersede Kernel-jar.1474.
Updated test will follow later.
=============== Diff against Kernel-jar.1476 ===============
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
- "Note: assigning nil to a runnable but not active process would freeze the image when self is scheduled to run."
+ priority ifNil: [priority := Processor activePriority].
+ suspendedContext := aContextOrNil ifNil: [self suspend. nil]!
- suspendedContext := aContext ifNil: [self suspend. nil]!
Marcel Taeumel uploaded a new version of Kernel to project The Treated Inbox:
http://source.squeak.org/treated/Kernel-jar.1476.mcz
==================== Summary ====================
Name: Kernel-jar.1476
Author: jar
Time: 7 June 2022, 5:26:20.195964 pm
UUID: 485a13c3-c632-aa4b-bc0d-a1a7fb9a3376
Ancestors: Kernel-jar.1475
- fix synchronization between the terminating process and the process that invoked the termination
- add further protection against multiple termination (it makes the code uglier but I can't help it)
- improve the comment (it's still too long and insufficient though ;) )
Complemented by additional tests (I'll bundle all of them in one changeset)
=============== Diff against Kernel-jar.1475 ===============
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."
"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."
- 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 |
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.
- suspendedContext := [context unwindTo: nil. terminator signal. self suspend] asContext.
- self priority: Processor activePriority; resume.
terminator wait]!
Marcel Taeumel uploaded a new version of Kernel to project The Treated Inbox:
http://source.squeak.org/treated/Kernel-jar.1475.mcz
==================== Summary ====================
Name: Kernel-jar.1475
Author: jar
Time: 4 June 2022, 10:31:44.661893 pm
UUID: 923d5402-9a8d-0a48-87d7-44c56400dd0c
Ancestors: Kernel-jar.1474
Fix a scenario when a delay or yield inside unwind blocks may cause control be handed over to the original process which may be assuming the termination has completed. (A higher priority for termination is indeed not a solution)
A process that invokes termination of another process is assumed to wait until the terminating process finishes unwinding itself. This may be useful during cleanup operations requiring e.g. waiting for resources etc.
A complementing test 'testTerminateWithDelayInUnwind' is part of the additional collection of tests in KernelTests-jar.428.
=============== Diff against Kernel-jar.1474 ===============
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."
+
- "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.
^[context unwindTo: nil. self suspend] asContext jump].
+ [] ensure: [ | terminator |
- [] ensure: [
self suspendAndReleaseCriticalSection.
context := suspendedContext ifNil: [^self].
+ terminator := Semaphore new.
+ suspendedContext := [context unwindTo: nil. terminator signal. self suspend] asContext.
+ self priority: Processor activePriority; resume.
+ terminator wait]!
- suspendedContext := [context unwindTo: nil. self suspend] asContext.
- self priority: (Processor activePriority + 1 min: Processor highestPriority); resume]!
Marcel Taeumel uploaded a new version of Kernel to project The Treated Inbox:
http://source.squeak.org/treated/Kernel-jar.1473.mcz
==================== Summary ====================
Name: Kernel-jar.1473
Author: jar
Time: 31 May 2022, 11:31:14.576719 pm
UUID: 53473e84-779a-8a4c-9bf5-32a53197cd05
Ancestors: Kernel-mt.1472
Fix a bug causing an endless stream of error windows poping up when debugging e.g.
[] newProcess
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.
=============== Diff against Kernel-mt.1472 ===============
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!