[squeak-dev] The Inbox: Kernel-eem.1183.mcz
commits at source.squeak.org
commits at source.squeak.org
Fri Jul 27 03:57:20 UTC 2018
A new version of Kernel was added to project The Inbox:
==================== Summary ====================
Time: 26 July 2018, 8:57:05.046828 pm
Fix the broken SemaphoreTest and MutexTest tests (test[Mutex|Semaphore][After|In]CriticalWait) by having Process>>terminate correctly release <criticalSection> marked methods. This is done by terminate's helper releaseCriticalSection: distinguishing between a context blocked on a blocking primitive (wait, primitiveEnterCriticalSection & primitiveTestAndSetOwnershipOfCriticalSection) and a runnable process that has been unblocked but has not advanced beyond the blocking send, presumably because it has been shut out by currently running higher priority processes.
Add InstructionStream>>selectorJustSentOrSelf (c.f. selectorToSendOrSelf) to support releaseCriticalSection:.
=============== Diff against Kernel-eem.1182 ===============
Item was added:
+ ----- Method: InstructionStream>>selectorJustSentOrSelf (in category 'scanning') -----
+ "If this instruction follows a send, answer the send's selector, otherwise answer self."
+ | method |
+ method := self method.
+ ^method encoderClass selectorToSendOrItselfFor: self in: method at: self previousPc!
Item was added:
+ ----- Method: Process>>releaseCriticalSection: (in category 'private') -----
+ releaseCriticalSection: runnable
+ "Figure out if we are terminating a process that is in the ensure: block of a critical section.
+ In this case, if the block has made progress, pop the suspendedContext so that we leave the
+ ensure: block inside the critical: without signaling the semaphore/exiting the primitive section,
+ since presumably this has already happened. But if it hasn't made progress but is beyond the
+ wait (which we can tell my the oldList being one of the runnable lists, i.e. a LinkedList, not a
+ Semaphore or Mutex, et al), then the ensure: block needs to be run."
+ | selectorJustSent |
+ (suspendedContext method pragmaAt: #criticalSection) ifNil: [^self].
+ selectorJustSent := suspendedContext selectorJustSentOrSelf.
+ "Receiver and/or argument blocks of ensure: in Semaphore>>critical: or Mutex>>#critical:"
+ suspendedContext isClosureContext ifTrue:
+ [suspendedContext sender selector == #ensure: ifTrue:
+ [| notWaitingButMadeNoProgress |
+ "Avoid running the ensure: block twice, popping it if it has already been run. If runnable
+ but at the wait, leave it in place. N.B. No need to check if the block receiver of ensure: has
+ not started to run (via suspendedContext pc = suspendedContext startpc) because ensure:
+ uses valueNoContextSwitch, and so there is no suspension point before the wait."
+ notWaitingButMadeNoProgress :=
+ and: [selectorJustSent == #wait
+ and: [suspendedContext sender selectorJustSentOrSelf == #valueNoContextSwitch]].
+ notWaitingButMadeNoProgress ifFalse:
+ [suspendedContext := suspendedContext home]].
+ "Either Semaphore>>critical: or Mutex>>#critical:. Is the process still blocked? If so, nothing further to do."
+ runnable ifFalse: [^self].
+ "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') -----
+ "Stop the process that the receiver represents forever.
+ Unwind to execute pending ensure:/ifCurtailed: blocks before terminating.
+ If the process is in the middle of a critical: critical section, release it properly."
- "Stop the process that the receiver represents forever. Unwind to execute pending ensure:/ifCurtailed: blocks before terminating."
| ctxt unwindBlock oldList |
self isActiveProcess ifTrue: [
ctxt := thisContext.
[ ctxt := ctxt findNextUnwindContextUpTo: nil.
] whileFalse: [
(ctxt tempAt: 2) ifNil:[
ctxt tempAt: 2 put: nil.
unwindBlock := ctxt tempAt: 1.
thisContext terminateTo: ctxt.
thisContext terminateTo: nil.
+ "Always suspend the process first so it doesn't accidentally get woken up.
+ N.B. If oldList is a LinkedList then the process is runnable. If it is a Semaphore/Mutex et al
+ then the process is blocked, and if it is nil then the process is already suspended."
- "Always suspend the process first so it doesn't accidentally get woken up"
oldList := self suspend.
+ suspendedContext ifNotNil:
+ ["Release any method marked with the <criticalSection> pragma.
+ The argument is whether the process is runnable."
+ self releaseCriticalSection: (oldList isNil or: [oldList class == LinkedList]).
- suspendedContext ifNotNil:[
- "Figure out if we are terminating a process that is in the ensure: block of a critical section.
- In this case, if the block has made progress, pop the suspendedContext so that we leave the
- ensure: block inside the critical: without signaling the semaphore/exiting the primitive section,
- since presumably this has already happened."
- (suspendedContext isClosureContext
- and: [(suspendedContext method pragmaAt: #criticalSection) notNil
- and: [suspendedContext startpc > suspendedContext closure startpc]]) ifTrue:
- [suspendedContext := suspendedContext home].
+ "If terminating a process halfways through an unwind, try to complete that unwind block first."
- "If we are terminating a process halfways through an unwind, try
- to complete that unwind block first."
(suspendedContext findNextUnwindContextUpTo: nil) ifNotNil:
(suspendedContext findContextSuchThat:[:c| c closure == (outer tempAt: 1)]) ifNotNil:
[:inner| "This is an unwind block currently under evaluation"
suspendedContext runUntilErrorOrReturnFrom: inner]].
ctxt := self popTo: suspendedContext bottomContext.
ctxt == suspendedContext bottomContext ifFalse:
[self debug: ctxt title: 'Unwind error during termination'].
"Set the context to its endPC for the benefit of isTerminated."
ctxt pc: ctxt endPC]]!
More information about the Squeak-dev