Branch: refs/heads/Cog
Home: https://github.com/OpenSmalltalk/opensmalltalk-vm
Commit: b2c016159deeb3c43cacdba9232538a4b5049dd1
https://github.com/OpenSmalltalk/opensmalltalk-vm/commit/b2c016159deeb3c43c…
Author: Marcel Taeumel <marcel.taeumel(a)hpi.de>
Date: 2023-11-20 (Mon, 20 Nov 2023)
Changed paths:
M platforms/unix/vm/sqUnixMain.c
Log Message:
-----------
For non-spur linux builds, fix description of "-mmap" option's default value. We cannot reuse the define of DefaultMmapSize directly since option() is not prepared for that printf-like argument signature. Similar to the default "UTF-8" for "-textenc" option. Hardcode 1024 for now. See define further up in sqUnixMain.c.
Branch: refs/heads/Cog
Home: https://github.com/OpenSmalltalk/opensmalltalk-vm
Commit: f98519f97262b1e114e3e667981bea9404683e31
https://github.com/OpenSmalltalk/opensmalltalk-vm/commit/f98519f97262b1e114…
Author: Marcel Taeumel <marcel.taeumel(a)hpi.de>
Date: 2023-11-20 (Mon, 20 Nov 2023)
Changed paths:
M building/win32x86/common/Makefile.plugin
Log Message:
-----------
For Windows builds, drop -mdll flag as it is not used by clang. In the future, we should also replace dllwrap with "gcc -shared" or "ld -shared" for preparing the shared libraries since dllwrap is deprecated.
Please announce candidates here. I can test riscv64 (Vision Five 2) as
well as a couple of aarch64 SoCs under Linux.
I mostly use Cuis, but could do Squeak if asked/needed. Let me know of
use cases to test other than unit test suite.
Thanks!
-KenD
Hi Eliot --
Squeak board and vm-dev in CC.
I can make a new OSVM release to then be bundled with Squeak 6.0 and 6.1alpha and maybe 5.3. And the Cuis community would also be happy.
See https://github.com/OpenSmalltalk/opensmalltalk-vm/issues/667
Are there any known showstoppers at this point? The stop-the-world GC seems to work fine. You fixed some ARM-related bugs during the last 12+ months. Threaded-FFI and incremental-GC are still in the making, but this should not stop us from giving the latest fixes to everybody.
Yes, the latest builds are also fine. But we could pick a Commit and then do some tests for a couple of days/weeks. Make a VM release before Christmas.
Best,
Marcel
P.S.: No, this is not about having a Squeak 6.1 release before Christmas. This is not going to happen. :-)
Leon Matthes uploaded a new version of VMMaker to project VM Maker:
http://source.squeak.org/VMMaker/VMMaker.threaded-LM.3345.mcz
==================== Summary ====================
Name: VMMaker.threaded-LM.3345
Author: LM
Time: 14 November 2023, 1:59:11.261792 pm
UUID: b6293fcf-0133-4cd7-b5de-d73eaa4b99b2
Ancestors: VMMaker.threaded-LM.3344
No longer use the list of awol processes within CogVMThread.
awol processes are now stored in the ProcessInExternalCodeTag.
Also do some refactorings to get threadAffinity and temporaryThreadAffinity in line.
=============== Diff against VMMaker.threaded-LM.3344 ===============
Item was changed:
----- Method: CoInterpreterMT class>>initializePrimitiveTable (in category 'initialization') -----
initializePrimitiveTable
super initializePrimitiveTable.
PrimNumberRelinquishProcessor := 230.
COGMTVM ifTrue:
[(227 to: 229) do:
[:pidx| self assert: (PrimitiveTable at: pidx + 1) = #primitiveFail].
PrimitiveTable
at: 227 + 1 put: #primitiveVMCurrentThreadId;
at: 228 + 1 put: #primitiveProcessBoundThreadId;
+ at: 229 + 1 put: #primitiveProcessBindToThreadAffinity]!
- at: 229 + 1 put: #primitiveProcessBindToThreadId]!
Item was removed:
- ----- Method: CoInterpreterMT>>affinedThreadId: (in category 'process primitive support') -----
- affinedThreadId: threadIdField
- "Answer the threadId of the thread threadIdField is temporarily bound to, or 0 if none."
- ^(objectMemory isIntegerObject: threadIdField)
- ifTrue: [(objectMemory integerValueOf: threadIdField) anyMask: 1 << ThreadIdShift - 1]
- ifFalse: [0]!
Item was added:
+ ----- Method: CoInterpreterMT>>bindProcess:toAffinity: (in category 'process primitive support') -----
+ bindProcess: aProcess toAffinity: newAffinity
+ "Change a Process's thread binding and answer 0, otherwise answer a suitable error code.
+ Cases:
+ process is unbound & unaffined
+ affinity 0 nothing to do
+ affinity non-zero ensure thread and bind
+ process is affined (temporarily bound to a thread for the duration of a surrender of ownership)
+ affinity = affined index nothing to do
+ affinity = 0 nothing to do
+ affinity ~= 0 && affinity ~= affined index fail
+ process is bound (permanently bound to a thread)
+ affinity = bound index nothing to do
+ affinity ~= bound index set bound index"
+ | threadIdField currentAffinity temporaryAffinity |
+ processHasThreadAffinity ifFalse:
+ [^PrimErrUnsupported].
+
+ threadIdField := self threadAffinityFieldOf: aProcess.
+ currentAffinity := self threadAffinityOfThreadID: threadIdField.
+ temporaryAffinity := self temporaryAffinedThreadId: threadIdField.
+
+ "If aProcess is affined (temporarily bound to) a thread then the operation can only
+ succeed if the newId is the same as that aProcess is affined to, or is zero (is unbinding)."
+ (self isTemporaryAffinedThreadId: threadIdField) ifTrue:
+ [(newAffinity = 0 or: [newAffinity = temporaryAffinity]) ifFalse:
+ [^PrimErrInappropriate]].
+
+ currentAffinity > 0 ifTrue:
+ [(self startThreadForThreadIndex: currentAffinity) ifFalse:
+ [^PrimErrLimitExceeded]].
+
+ self setThreadIdFieldOfProcess: aProcess toAffinity: newAffinity andTemporaryAffinity: temporaryAffinity.
+ ^nil!
Item was removed:
- ----- Method: CoInterpreterMT>>bindProcess:toId: (in category 'process primitive support') -----
- bindProcess: aProcess toId: newId
- "Change a Process's thread binding and answer 0, otherwise answer a suitable error code.
- Cases:
- process is unbound & unaffined
- affinity 0 nothing to do
- affinity non-zero ensure thread and bind
- process is affined (temporarily bound to a thread for the duration of a surrender of ownership)
- affinity = affined index nothing to do
- affinity = 0 nothing to do
- affinity ~= 0 && affinity ~= affined index fail
- process is bound (permanently bound to a thread)
- affinity = bound index nothing to do
- affinity ~= bound index set bound index"
- | threadIdField ownerIndex affinedId |
- processHasThreadAffinity ifFalse:
- [^PrimErrUnsupported].
-
- threadIdField := self threadAffinityFieldOf: aProcess.
- ownerIndex := self ownerIndexOfThreadId: threadIdField.
-
- "If aProcess is affined (temporarily bound to) a thread then the operation can only
- succeed if the newId is the same as that aProcess is affined to, or is zero (is unbinding)."
- (self isAffinedThreadId: threadIdField) ifTrue:
- [affinedId := self affinedThreadId: threadIdField.
- (newId = 0
- or: [newId = affinedId]) ifTrue:
- [self setThreadIdFieldOfProcess: aProcess to: newId << ThreadIdShift + affinedId.
- ^nil].
- ^PrimErrInappropriate].
-
- ownerIndex > 0 ifTrue:
- [(self startThreadForThreadIndex: ownerIndex) ifFalse:
- [^PrimErrLimitExceeded]].
-
- self setThreadIdFieldOfProcess: aProcess to: newId << ThreadIdShift.
- ^nil!
Item was removed:
- ----- Method: CoInterpreterMT>>isAffinedProcess: (in category 'process primitive support') -----
- isAffinedProcess: aProcess
- ^self isAffinedThreadId: (self threadAffinityFieldValueOf: aProcess)!
Item was removed:
- ----- Method: CoInterpreterMT>>isAffinedThreadId: (in category 'process primitive support') -----
- isAffinedThreadId: threadIdField
- "Answer if the threadId has the bits set indicating the thread it is temporarily bound to."
- ^(self affinedThreadId: threadIdField) ~= 0!
Item was added:
+ ----- Method: CoInterpreterMT>>isTemporaryAffinedProcess: (in category 'process primitive support') -----
+ isTemporaryAffinedProcess: aProcess
+ ^self isTemporaryAffinedThreadId: (self threadAffinityFieldValueOf: aProcess)!
Item was added:
+ ----- Method: CoInterpreterMT>>isTemporaryAffinedThreadId: (in category 'process primitive support') -----
+ isTemporaryAffinedThreadId: threadIdField
+ "Answer if the threadId has the bits set indicating the thread it is temporarily bound to."
+ ^(self temporaryAffinedThreadId: threadIdField) ~= 0!
Item was removed:
- ----- Method: CoInterpreterMT>>ownerIndexOfThreadId: (in category 'process primitive support') -----
- ownerIndexOfThreadId: threadId
- ^(objectMemory isIntegerObject: threadId)
- ifTrue: ["We need a signed shift here (>>>), as otherwise we lose the sign of the threadId."
- (objectMemory integerValueOf: threadId) >>> ThreadIdShift]
- ifFalse: [0]!
Item was added:
+ ----- Method: CoInterpreterMT>>popProcessWithTemporaryAffinity:fromList: (in category 'process primitive support') -----
+ popProcessWithTemporaryAffinity: anAffinity fromList: aList
+ "Find the first process from the list that is temporarily affined to the given affinity.
+ Remove this process from the list and return it.
+ This is used by the preempt/restore flow to find the process that is to be restored."
+ | firstLink lastLink nextLink tempLink theProcess |
+ self assert: (anAffinity ~= 0).
+ self deny: (objectMemory isForwarded: aList).
+ "any process on the list could have been becomed, so use a read barrier..."
+ firstLink := objectMemory followField: FirstLinkIndex ofObject: aList.
+ lastLink := objectMemory followField: LastLinkIndex ofObject: aList.
+ "fail if any link doesn't look like a process..."
+ ((objectMemory isPointers: firstLink)
+ and: [(objectMemory numSlotsOf: firstLink) > MyListIndex]) ifFalse:
+ [^ objectMemory nilObject].
+
+ (firstLink ~= objectMemory nilObject and: [(self temporaryAffinityOfProcess: firstLink) = anAffinity])
+ ifTrue:
+ [theProcess := firstLink.
+ nextLink := objectMemory followField: NextLinkIndex ofObject: firstLink.
+ objectMemory storePointer: FirstLinkIndex ofObject: aList withValue: nextLink.
+ firstLink = lastLink ifTrue:
+ [objectMemory storePointerUnchecked: LastLinkIndex ofObject: aList withValue: objectMemory nilObject]]
+ ifFalse:
+ [tempLink := firstLink.
+ [
+ nextLink := objectMemory followField: NextLinkIndex ofObject: tempLink.
+ "fail if any link doesn't look like a process..."
+ ((objectMemory isPointers: nextLink)
+ and: [(objectMemory numSlotsOf: nextLink) > MyListIndex]) ifFalse:
+ [^ objectMemory nilObject].
+ (self temporaryAffinityOfProcess: nextLink) = anAffinity]
+ whileFalse: [tempLink := nextLink].
+
+ theProcess := nextLink.
+ nextLink := objectMemory fetchPointer: NextLinkIndex ofObject: nextLink.
+ objectMemory storePointer: NextLinkIndex ofObject: tempLink withValue: nextLink.
+ theProcess = lastLink ifTrue:
+ [objectMemory storePointer: LastLinkIndex ofObject: aList withValue: tempLink]].
+ objectMemory storePointerUnchecked: NextLinkIndex ofObject: theProcess withValue: objectMemory nilObject.
+ ^ theProcess!
Item was changed:
----- Method: CoInterpreterMT>>preemptDisowningThread (in category 'vm scheduling') -----
preemptDisowningThread
"Set the relevant state for disowningVMThread so that it can resume after
being preempted and set disowningVMThread to nil to indicate preemption.
N.B. This should only be sent from checkPreemptionOfDisowningThread.
There are essentially four things to do.
a) save the VM's notion of the current C stack pointers; these are pointers
into a thread's stack and must be saved and restored in thread switch.
b) save the VM's notion of the current Smalltalk execution point. This is
simply the suspend half of a process switch that saves the current context
in the current process.
c) add the process to the thread's set of AWOL processes so that the scheduler
won't try to run the process while the thread has disowned the VM.
d) save the in-primitive VM state, newMethod and argumentCount
ownVM: will restore the VM context as of disownVM: from the above when it
finds it has been preempted."
| activeProc activeContext preemptedThread |
<var: #preemptedThread type: #'CogVMThread *'>
<inline: false>
self assert: disowningVMThread notNil.
self assert: (disowningVMThread vmThreadState = CTMUnavailable
or: [disowningVMThread vmThreadState = CTMWantingOwnership]).
self assertCStackPointersBelongToDisowningThread.
cogit recordEventTrace ifTrue:
[self recordTrace: TracePreemptDisowningThread
thing: (objectMemory integerObjectOf: disowningVMThread index)
source: 0].
disowningVMThread cStackPointer: CStackPointer.
disowningVMThread cFramePointer: CFramePointer.
activeProc := self activeProcess.
- "To make sure the process isn't garbage collected, add the Process to the list of processes that are in external code.
- This should also be helpful if we want to fail any processes that are AWOL when restarting an image."
- self addFirstLink: activeProc toList: (objectMemory splObj: ProcessInExternalCodeTag).
activeContext := self ensureFrameIsMarried: framePointer SP: stackPointer.
objectMemory
storePointer: SuspendedContextIndex
ofObject: activeProc
withValue: activeContext.
"The instructionPointer must be pushed because the convention for inactive stack pages is that the
instructionPointer is top of stack. We need to know if this primitive is called from machine code
because the invariant that the return pc of an interpreter callee calling a machine code caller is
ceReturnToInterpreterPC must be maintained."
self push: instructionPointer.
self externalWriteBackHeadFramePointers.
"Since pushing the awol process may realloc disowningVMThread we need to reassign.
But since we're going to nil disowningVMThread anyway we can assign to a local."
- cogThreadManager pushAWOLProcess: activeProc on: disowningVMThread.
preemptedThread := disowningVMThread.
disowningVMThread := nil.
+
+ "Store the process in the ProcessInExternalCodeTag special object (a LinkedList).
+ This ensures:
+ - The process isn't garbage collected
+ - If the process is moved due to GC, we can still find it
+ - The process is available from inside the image
+ On restore we can use the temporaryThreadAffinity to find the last process that was disowned from the preempted thread.
+ This therefore creates a LIFO stack for each thread which are all interleaved in this one list."
+ self assert: (self isTemporaryAffinedProcess: activeProc) not.
+ self setTemporaryThreadAffinityOfProcess: activeProc to: preemptedThread index.
+ self addFirstLink: activeProc toList: (objectMemory splObj: ProcessInExternalCodeTag).
+
- (self threadAffinityOfProcess: activeProc) = 0 ifTrue:
- [self setTemporaryThreadAffinityOfProcess: activeProc to: preemptedThread index bind: false].
preemptedThread
newMethodOrNull: newMethod;
argumentCount: argumentCount;
inMachineCode: instructionPointer <= objectMemory startOfMemory!
Item was added:
+ ----- Method: CoInterpreterMT>>primitiveProcessBindToThreadAffinity (in category 'process primitives') -----
+ primitiveProcessBindToThreadAffinity
+ "Attempt to bind the receiver to the thread affinity of the argument or nil, where the receiver is a Process.
+ The thread affinity may be an integer where:
+ 0 - means no thread affinity, the process is free to run on any thread.
+ > 0 - positive values mean the process has to run on the thread with this specific index.
+ < 0 - negative values mean the process may run on on any thread **APART** from the thread
+ with the absolute value of the index.
+
+ Usually values of 1, -1 and 0 are used.
+ Thread number 1 is the thread the VM started with. On some OSes this thread has special priviliges.
+ I.e. on macOS only thread 1 can make draw calls.
+ Therefore it is mostly important whether a thread must run on thread 1, must **not** run on thread 1
+ or whether it doesn't care.
+
+ If successful the VM will ensure that there is at least one compatible thread active."
+ | aProcess affinity waitingPriority activePriority |
+ <export: true>
+ self cCode: [] inSmalltalk: [cogThreadManager isNil ifTrue: [^self primitiveFail]].
+ processHasThreadAffinity ifFalse:
+ [^self primitiveFailFor: PrimErrUnsupported].
+ affinity := self stackTop.
+ aProcess := self stackValue: 1.
+ ((affinity = objectMemory nilObject or: [(objectMemory isIntegerObject: affinity)
+ and: [affinity ~= (objectMemory integerObjectOf: 0)]])
+ and: [(objectMemory isPointers: aProcess)
+ and: [(objectMemory slotSizeOf: aProcess) >= (ThreadIdIndex + 1)]]) ifFalse:
+ [^self primitiveFailFor: PrimErrBadArgument].
+ affinity := affinity = objectMemory nilObject ifTrue: [0] ifFalse: [objectMemory integerValueOf: affinity].
+ affinity abs >= cogThreadManager maxNumThreads ifTrue:
+ [^self primitiveFailFor: PrimErrLimitExceeded].
+ (self bindProcess: aProcess toAffinity: affinity) ifNotNil:
+ [:ec| ^self primitiveFailFor: ec].
+ self methodReturnReceiver.
+
+ waitingPriority := self getMaxWaitingPriority.
+ activePriority := self quickFetchInteger: PriorityIndex ofObject: aProcess.
+ affinity := self threadAffinityOfProcess: aProcess.
+ (aProcess = self activeProcess
+ and: [(activeProcessAffined := affinity ~= 0)
+ and: [(cogThreadManager vmOwnerIsCompatibleWith: affinity) not]]) ifTrue:
+ [activePriority < waitingPriority ifTrue:
+ [self reduceWaitingPriorityFrom: waitingPriority to: activePriority "TODO: Check if this is correct?"].
+ self threadSwitchIfNecessary: aProcess from: CSThreadBind]!
Item was removed:
- ----- Method: CoInterpreterMT>>primitiveProcessBindToThreadId (in category 'process primitives') -----
- primitiveProcessBindToThreadId
- "Attempt to bind the receiver to the thread with the id of the argument or nil, where the receiver is a Process.
- If successful the VM will ensure that there are at least id many threads active."
- | aProcess id waitingPriority activePriority |
- <export: true>
- self cCode: [] inSmalltalk: [cogThreadManager isNil ifTrue: [^self primitiveFail]].
- processHasThreadAffinity ifFalse:
- [^self primitiveFailFor: PrimErrUnsupported].
- id := self stackTop.
- aProcess := self stackValue: 1.
- ((id = objectMemory nilObject or: [(objectMemory isIntegerObject: id)
- and: [id ~= (objectMemory integerObjectOf: 0)]])
- and: [(objectMemory isPointers: aProcess)
- and: [(objectMemory slotSizeOf: aProcess) >= (ThreadIdIndex + 1)]]) ifFalse:
- [^self primitiveFailFor: PrimErrBadArgument].
- id := id = objectMemory nilObject ifTrue: [0] ifFalse: [objectMemory integerValueOf: id].
- id abs >= cogThreadManager maxNumThreads ifTrue:
- [^self primitiveFailFor: PrimErrLimitExceeded].
- (self bindProcess: aProcess toId: id) ifNotNil:
- [:ec| ^self primitiveFailFor: ec].
- self methodReturnReceiver.
-
- waitingPriority := self getMaxWaitingPriority.
- activePriority := self quickFetchInteger: PriorityIndex ofObject: aProcess.
- id := self threadAffinityOfProcess: aProcess.
- (aProcess = self activeProcess
- and: [(activeProcessAffined := id ~= 0)
- and: [(cogThreadManager vmOwnerIsCompatibleWith: id) not]]) ifTrue:
- [activePriority < waitingPriority ifTrue:
- [self reduceWaitingPriorityFrom: waitingPriority to: activePriority "TODO: Check if this is correct?"].
- self threadSwitchIfNecessary: aProcess from: CSThreadBind]!
Item was added:
+ ----- Method: CoInterpreterMT>>removeFirstProcessWithTemporaryAffinity:fromList: (in category 'process primitive support') -----
+ removeFirstProcessWithTemporaryAffinity: anAffinity fromList: aList
+
+ "Find the first process from the list that is temporarily affined to the given affinity.
+ Remove this process from the list and return it.
+ This is used by the preempt/restore flow to find the process that is to be restored."
+ | firstLink lastLink nextLink tempLink theProcess |
+ self assert: (anAffinity ~= 0).
+ self deny: (objectMemory isForwarded: aList).
+ "any process on the list could have been becomed, so use a read barrier..."
+ firstLink := objectMemory followField: FirstLinkIndex ofObject: aList.
+ lastLink := objectMemory followField: LastLinkIndex ofObject: aList.
+ (firstLink ~= objectMemory nilObject and: [(self temporaryAffinityOfProcess: firstLink) = anAffinity])
+ ifTrue:
+ [theProcess := firstLink.
+ nextLink := objectMemory followField: NextLinkIndex ofObject: firstLink.
+ objectMemory storePointer: FirstLinkIndex ofObject: aList withValue: nextLink.
+ firstLink = lastLink ifTrue:
+ [objectMemory storePointerUnchecked: LastLinkIndex ofObject: aList withValue: objectMemory nilObject]]
+ ifFalse:
+ [tempLink := firstLink.
+ ["fail if any link doesn't look like a process..."
+ ((objectMemory isPointers: tempLink)
+ and: [(objectMemory numSlotsOf: tempLink) > MyListIndex]) ifFalse:
+ [^false].
+ nextLink := objectMemory followField: NextLinkIndex ofObject: tempLink.
+ nextLink ~= objectMemory nilObject and: [(self temporaryAffinityOfProcess: nextLink) = anAffinity]]
+ whileFalse: [tempLink := nextLink].
+
+ nextLink = objectMemory nilObject ifTrue: [^ false].
+
+ theProcess := nextLink.
+ nextLink := objectMemory fetchPointer: NextLinkIndex ofObject: nextLink.
+ objectMemory storePointer: NextLinkIndex ofObject: tempLink withValue: nextLink.
+ theProcess = lastLink ifTrue:
+ [objectMemory storePointer: LastLinkIndex ofObject: aList withValue: tempLink]].
+ objectMemory storePointerUnchecked: NextLinkIndex ofObject: theProcess withValue: objectMemory nilObject.
+ ^true!
Item was changed:
----- Method: CoInterpreterMT>>restoreVMStateFor:andFlags: (in category 'vm scheduling') -----
restoreVMStateFor: vmThread andFlags: flags
"We've been preempted; we must restore state and update the threadId
in our process, and may have to put the active process to sleep."
+ | sched activeProc myProc |
- | sched activeProc myProc removedFromList |
sched := self schedulerPointer.
activeProc := objectMemory fetchPointer: ActiveProcessIndex ofObject: sched.
(flags anyMask: OwnVMForeignThreadFlag)
ifTrue:
[self assert: foreignCallbackProcessSlot == ForeignCallbackProcess.
myProc := objectMemory splObj: foreignCallbackProcessSlot.
self assert: myProc ~= objectMemory nilObject.
objectMemory splObj: foreignCallbackProcessSlot put: objectMemory nilObject]
+ ifFalse: [myProc := self popProcessWithTemporaryAffinity: vmThread index fromList: (objectMemory splObj: ProcessInExternalCodeTag)].
+ self assert: (myProc ~= objectMemory nilObject and: [activeProc ~= myProc]).
- ifFalse: [myProc := cogThreadManager popAWOLProcess: vmThread].
- self assert: activeProc ~= myProc.
(activeProc ~= objectMemory nilObject
and: [(objectMemory fetchPointer: MyListIndex ofObject: activeProc) = objectMemory nilObject]) ifTrue:
[self putToSleep: activeProc yieldingIf: preemptionYields].
- "Make sure to remove the Process from the list of processes that are in external code, such that it may be GCed in future."
- self assert: (objectMemory fetchPointer: MyListIndex ofObject: myProc) = (objectMemory splObj: ProcessInExternalCodeTag).
- removedFromList := self removeProcess: myProc fromList: (objectMemory splObj: ProcessInExternalCodeTag).
- self assert: removedFromList. "We shouldn't put code that does important things into an assert, so save result and check that."
objectMemory
storePointerUnchecked: MyListIndex ofObject: myProc withValue: objectMemory nilObject;
storePointer: ActiveProcessIndex ofObject: sched withValue: myProc.
-
"Only unaffine if the process was affined at this level and did not become bound in the interim."
((flags anyMask: ProcessUnaffinedOnDisown)
and: [(self isBoundProcess: myProc) not]) ifTrue:
+ [self setTemporaryThreadAffinityOfProcess: myProc to: 0].
- [self setTemporaryThreadAffinityOfProcess: myProc to: 0 bind: false].
self initPrimCall.
self cCode:
[self externalSetStackPageAndPointersForSuspendedContextOfProcess: myProc]
inSmalltalk:
["Bypass the no-offset stack depth check in the simulator's externalSetStackPageAndPointersForSuspendedContextOfProcess:"
super externalSetStackPageAndPointersForSuspendedContextOfProcess: myProc.
"We're in ownVM:, hence in a primitive, hence need to include the argument count"
(self isMachineCodeFrame: framePointer) ifTrue:
[self maybeCheckStackDepth: vmThread argumentCount
sp: stackPointer
pc: instructionPointer]].
"If this primitive is called from machine code maintain the invariant that the return pc
of an interpreter callee calling a machine code caller is ceReturnToInterpreterPC."
(vmThread inMachineCode
and: [instructionPointer >= objectMemory startOfMemory]) ifTrue:
[self iframeSavedIP: framePointer put: instructionPointer.
instructionPointer := cogit ceReturnToInterpreterPC].
newMethod := vmThread newMethodOrNull.
argumentCount := vmThread argumentCount.
vmThread newMethodOrNull: nil.
self cCode: '' inSmalltalk:
[| range |
range := self cStackRangeForThreadIndex: vmThread index.
self assert: ((range includes: vmThread cStackPointer) and: [range includes: vmThread cFramePointer])].
self setCFramePointer: vmThread cFramePointer setCStackPointer: vmThread cStackPointer.
self assert: newMethod notNil
!
Item was added:
+ ----- Method: CoInterpreterMT>>setTemporaryThreadAffinityOfProcess:to: (in category 'process primitive support') -----
+ setTemporaryThreadAffinityOfProcess: aProcess to: anIndex
+ "When a thread is disowned for threading, it will temporarily affine the process to itself.
+ Therefore we can make sure no one else accidentally tries to re-bind the process whilst it is AWOL."
+ | threadAffinity |
+ threadAffinity := self threadAffinityOfProcess: aProcess.
+ self setThreadIdFieldOfProcess: aProcess toAffinity: threadAffinity andTemporaryAffinity: anIndex.!
Item was removed:
- ----- Method: CoInterpreterMT>>setTemporaryThreadAffinityOfProcess:to:bind: (in category 'process primitive support') -----
- setTemporaryThreadAffinityOfProcess: aProcess to: anIndex bind: bind
- | threadId |
- threadId := anIndex = 0
- ifTrue: [objectMemory nilObject]
- ifFalse: [objectMemory integerObjectOf: (anIndex << 1) + (bind ifTrue: [1] ifFalse: [0])].
- objectMemory storePointerUnchecked: ThreadIdIndex ofObject: aProcess withValue: threadId!
Item was removed:
- ----- Method: CoInterpreterMT>>setThreadAffinityOfProcess:to:bind: (in category 'process primitive support') -----
- setThreadAffinityOfProcess: aProcess to: anIndex bind: bind
- | threadId |
- threadId := anIndex = 0
- ifTrue: [objectMemory nilObject]
- ifFalse: [objectMemory integerObjectOf: (anIndex << 1) + (bind ifTrue: [1] ifFalse: [0])].
- objectMemory storePointerUnchecked: ThreadIdIndex ofObject: aProcess withValue: threadId!
Item was removed:
- ----- Method: CoInterpreterMT>>setThreadIdFieldOfProcess:to: (in category 'process primitive support') -----
- setThreadIdFieldOfProcess: aProcess to: threadIdField
- | threadIdSlot |
- threadIdSlot := threadIdField = 0
- ifTrue: [objectMemory nilObject]
- ifFalse: [objectMemory integerObjectOf: threadIdField].
- objectMemory storePointerUnchecked: ThreadIdIndex ofObject: aProcess withValue: threadIdSlot!
Item was added:
+ ----- Method: CoInterpreterMT>>setThreadIdFieldOfProcess:toAffinity:andTemporaryAffinity: (in category 'process primitive support') -----
+ setThreadIdFieldOfProcess: aProcess toAffinity: threadAffinity andTemporaryAffinity: anIndex
+ | threadIdSlot bits |
+ self assert: anIndex >= 0.
+ self assert: anIndex <= cogThreadManager maxNumThreads.
+ self assert: threadAffinity >= cogThreadManager maxNumThreads negated.
+ self assert: threadAffinity <= cogThreadManager maxNumThreads.
+
+ anIndex > 0 ifTrue: [self assert: (cogThreadManager threadIndex: anIndex isCompatibleWith: threadAffinity)].
+
+ bits := threadAffinity << ThreadIdShift + anIndex.
+
+ threadIdSlot := bits = 0
+ ifTrue: [objectMemory nilObject]
+ ifFalse: [objectMemory integerObjectOf: bits].
+ objectMemory storePointerUnchecked: ThreadIdIndex ofObject: aProcess withValue: threadIdSlot!
Item was added:
+ ----- Method: CoInterpreterMT>>temporaryAffinedThreadId: (in category 'process primitive support') -----
+ temporaryAffinedThreadId: threadIdField
+ "Answer the threadId of the thread threadIdField is temporarily bound to, or 0 if none."
+ ^(objectMemory isIntegerObject: threadIdField)
+ ifTrue: [(objectMemory integerValueOf: threadIdField) bitAnd: 1 << ThreadIdShift - 1]
+ ifFalse: [0]!
Item was added:
+ ----- Method: CoInterpreterMT>>temporaryAffinityOfProcess: (in category 'process primitive support') -----
+ temporaryAffinityOfProcess: aProcess
+ "Answer the threadId of the thread threadIdField is temporarily bound to, or 0 if none."
+ ^ self temporaryAffinedThreadId: (self threadAffinityFieldOf: aProcess)!
Item was changed:
----- Method: CoInterpreterMT>>threadAffinityOfProcess: (in category 'process primitive support') -----
threadAffinityOfProcess: aProcess
+ ^self threadAffinityOfThreadID: (self threadAffinityFieldOf: aProcess)!
- ^self ownerIndexOfThreadId: (self threadAffinityFieldOf: aProcess)!
Item was added:
+ ----- Method: CoInterpreterMT>>threadAffinityOfThreadID: (in category 'process primitive support') -----
+ threadAffinityOfThreadID: threadId
+ ^(objectMemory isIntegerObject: threadId)
+ ifTrue: ["We need a signed shift here (>>>), as otherwise we lose the sign of the threadId."
+ (objectMemory integerValueOf: threadId) >>> ThreadIdShift]
+ ifFalse: [0]!
Item was changed:
----- Method: CoInterpreterPrimitives>>primitiveSuspend (in category 'process primitives') -----
primitiveSuspend
"Primitive #88. Suspend the receiver, aProcess, such that it can be executed again
by sending #resume. If the given process is not the active process, take it off
its corresponding list. The primitive returns the list the receiver was previously on.
c.f. primitiveSuspendBackingUpV1,#568 & primitiveSuspendBackingUpV2,#578"
| process myList myContext ok |
process := self stackTop.
process = self activeProcess ifTrue:
[| inInterpreter |
"We're going to switch process, either to an interpreted frame or a machine
code frame. To know whether to return or enter machine code we have to
know from whence we came. We could have come from the interpreter,
either directly or via a machine code primitive. We could have come from
machine code. The instructionPointer tells us where from:"
self stackTopPut: objectMemory nilObject.
inInterpreter := instructionPointer >= objectMemory startOfMemory.
self transferTo: self wakeHighestPriority from: CSSuspend.
^self forProcessPrimitiveReturnToExecutivePostContextSwitch: inInterpreter].
myList := objectMemory fetchPointer: MyListIndex ofObject: process.
myContext := objectMemory fetchPointer: SuspendedContextIndex ofObject: process.
((objectMemory isPointers: myList)
and: [(objectMemory numSlotsOf: myList) > LastLinkIndex
and: [(objectMemory isContext: myContext)
and: [self isResumableContext: myContext]]]) ifFalse:
[^self primitiveFailFor: PrimErrBadReceiver].
ok := self removeProcess: process fromList: myList.
ok ifFalse:
[^self primitiveFailFor: PrimErrOperationFailed].
- objectMemory storePointerUnchecked: MyListIndex ofObject: process withValue: objectMemory nilObject.
self stackTopPut: myList!
Item was changed:
----- Method: CoInterpreterPrimitives>>primitiveSuspendBackingUpV1 (in category 'process primitives') -----
primitiveSuspendBackingUpV1
"Primitive #568. Suspend the receiver, aProcess, such that it can be executed again
by sending #resume. If the given process is not the active process, take it off
its corresponding list. If the list was not its run queue assume it was on some
condition variable (Semaphore, Mutex) and back up its pc to the send that
invoked the wait state the process entered. Hence when the process resumes
it will reenter the wait state. Answer the list the receiver was previously on,
unless it was the activeProcess, in which case answer nil.
c.f. primitiveSuspend,#88 & primitiveSuspendBackingUpV2,#578"
| process myList myContext ok |
process := self stackTop.
process = self activeProcess ifTrue:
[| inInterpreter |
"We're going to switch process, either to an interpreted frame or a machine
code frame. To know whether to return or enter machine code we have to
know from whence we came. We could have come from the interpreter,
either directly or via a machine code primitive. We could have come from
machine code. The instructionPointer tells us where from:"
self stackTopPut: objectMemory nilObject.
inInterpreter := instructionPointer >= objectMemory startOfMemory.
self transferTo: self wakeHighestPriority from: CSSuspend.
^self forProcessPrimitiveReturnToExecutivePostContextSwitch: inInterpreter].
myList := objectMemory fetchPointer: MyListIndex ofObject: process.
myContext := objectMemory fetchPointer: SuspendedContextIndex ofObject: process.
((objectMemory isPointers: myList)
and: [(objectMemory numSlotsOf: myList) > LastLinkIndex
and: [(objectMemory isContext: myContext)
and: [self isResumableContext: myContext]]]) ifFalse:
[^self primitiveFailFor: PrimErrBadReceiver].
ok := self removeProcess: process fromList: myList.
ok ifFalse:
[^self primitiveFailFor: PrimErrOperationFailed].
- objectMemory storePointerUnchecked: MyListIndex ofObject: process withValue: objectMemory nilObject.
(objectMemory fetchClassTagOfNonImm: myList) ~= classLinkedListClassTag ifTrue:
[self backupContext: myContext toBlockingSendTo: myList].
self stackTopPut: myList!
Item was changed:
----- Method: CoInterpreterPrimitives>>primitiveSuspendBackingUpV2 (in category 'process primitives') -----
primitiveSuspendBackingUpV2
"Primitive #578. Suspend the receiver, aProcess, such that it can be executed again
by sending #resume. If the given process is not the active process, take it off
its corresponding list. If the list was not its run queue assume it was on some
condition variable (Semaphore, Mutex) and back up its pc to the send that
invoked the wait state the process entered. Hence when the process resumes
it will reenter the wait state. Answer the list the receiver was previously on iff
it was not active and not blocked, otherwise answer nil.
c.f. primitiveSuspend,#88 & primitiveSuspendBackingUpV1,#568,
which always answer the list the process was on, even if blocked."
<export: true>
| process myList myContext ok |
process := self stackTop.
process = self activeProcess ifTrue:
[| inInterpreter |
"We're going to switch process, either to an interpreted frame or a machine
code frame. To know whether to return or enter machine code we have to
know from whence we came. We could have come from the interpreter,
either directly or via a machine code primitive. We could have come from
machine code. The instructionPointer tells us where from:"
self stackTopPut: objectMemory nilObject.
inInterpreter := instructionPointer >= objectMemory startOfMemory.
self transferTo: self wakeHighestPriority from: CSSuspend.
^self forProcessPrimitiveReturnToExecutivePostContextSwitch: inInterpreter].
myList := objectMemory fetchPointer: MyListIndex ofObject: process.
myContext := objectMemory fetchPointer: SuspendedContextIndex ofObject: process.
((objectMemory isPointers: myList)
and: [(objectMemory numSlotsOf: myList) > LastLinkIndex
and: [(objectMemory isContext: myContext)
and: [self isResumableContext: myContext]]]) ifFalse:
[^self primitiveFailFor: PrimErrBadReceiver].
ok := self removeProcess: process fromList: myList.
ok ifFalse:
[^self primitiveFailFor: PrimErrOperationFailed].
- objectMemory storePointerUnchecked: MyListIndex ofObject: process withValue: objectMemory nilObject.
(objectMemory fetchClassTagOfNonImm: myList) ~= classLinkedListClassTag
ifTrue:
[self backupContext: myContext toBlockingSendTo: myList.
self stackTopPut: objectMemory nilObject]
ifFalse:
[self stackTopPut: myList]!
Item was removed:
- ----- Method: CogThreadManager>>growAWOLProcessesOf: (in category 'public api') -----
- growAWOLProcessesOf: vmThread
- <var: #vmThread type: #'CogVMThread*'>
- self
- cCode: [vmThread awolProcesses: (self realloc: vmThread awolProcesses _: vmThread awolProcLength + AWOLProcessesIncrement * (self sizeof: #sqInt))]
- inSmalltalk: [vmThread awolProcesses setObject: vmThread awolProcesses object, (Array new: AWOLProcessesIncrement)].
- vmThread awolProcLength: vmThread awolProcLength + AWOLProcessesIncrement.!
Item was removed:
- ----- Method: CogThreadManager>>pushAWOLProcess:on: (in category 'public api') -----
- pushAWOLProcess: awolProcess on: vmThread
- <var: #vmThread type: #'CogVMThread *'>
- self assert: (vmThread awolProcIndex between: 0 and: vmThread awolProcLength).
- vmThread awolProcIndex >= vmThread awolProcLength
- ifTrue: [self growAWOLProcessesOf: vmThread].
-
- self assert: vmThread awolProcLength > vmThread awolProcIndex.
- vmThread awolProcesses at: vmThread awolProcIndex put: awolProcess.
- vmThread awolProcIndex: vmThread awolProcIndex + 1.!
Item was changed:
----- Method: CogThreadManager>>startThreadForThreadIndex: (in category 'scheduling') -----
startThreadForThreadIndex: index
+ self assert: index > 0.
index > numThreads ifTrue:
[(self growThreadInfosToAtLeast: index) ifFalse:
[^false]].
^self startThreadForThreadInfo: (self vmThreadAt: index)!
Item was removed:
- ----- Method: CogVMSimulator>>bindProcess:toId: (in category 'multi-threading simulation switch') -----
- bindProcess: aProcess toId: newId
- "This method includes or excludes CoInterpreterMT methods as required.
- Auto-generated by CogVMSimulator>>ensureMultiThreadingOverridesAreUpToDate"
-
- ^self perform: #bindProcess:toId:
- withArguments: {aProcess. newId}
- inSuperclass: (cogThreadManager ifNil: [CoInterpreterPrimitives] ifNotNil: [CoInterpreterMT])!
Item was removed:
- ----- Method: CogVMSimulator>>isAffinedProcess: (in category 'multi-threading simulation switch') -----
- isAffinedProcess: aProcess
- "This method includes or excludes CoInterpreterMT methods as required.
- Auto-generated by CogVMSimulator>>ensureMultiThreadingOverridesAreUpToDate"
-
- ^self perform: #isAffinedProcess:
- withArguments: {aProcess}
- inSuperclass: (cogThreadManager ifNil: [CoInterpreterPrimitives] ifNotNil: [CoInterpreterMT])!
Item was removed:
- ----- Method: CogVMSimulator>>isAffinedThreadId: (in category 'multi-threading simulation switch') -----
- isAffinedThreadId: threadId
- "This method includes or excludes CoInterpreterMT methods as required.
- Auto-generated by CogVMSimulator>>ensureMultiThreadingOverridesAreUpToDate"
-
- ^self perform: #isAffinedThreadId:
- withArguments: {threadId}
- inSuperclass: (cogThreadManager ifNil: [CoInterpreterPrimitives] ifNotNil: [CoInterpreterMT])!
Item was removed:
- ----- Method: CogVMSimulator>>ownerIndexOfThreadId: (in category 'multi-threading simulation switch') -----
- ownerIndexOfThreadId: threadId
- "This method includes or excludes CoInterpreterMT methods as required.
- Auto-generated by CogVMSimulator>>ensureMultiThreadingOverridesAreUpToDate"
-
- ^self perform: #ownerIndexOfThreadId:
- withArguments: {threadId}
- inSuperclass: (cogThreadManager ifNil: [CoInterpreterPrimitives] ifNotNil: [CoInterpreterMT])!
Item was added:
+ ----- Method: CogVMSimulator>>primitiveProcessBindToThreadAffinity (in category 'multi-threading simulation switch') -----
+ primitiveProcessBindToThreadAffinity
+ "This method includes or excludes CoInterpreterMT methods as required.
+ Auto-generated by CogVMSimulator>>ensureMultiThreadingOverridesAreUpToDate"
+
+ ^self perform: #primitiveProcessBindToThreadAffinity
+ withArguments: {}
+ inSuperclass: (cogThreadManager ifNil: [CoInterpreterPrimitives] ifNotNil: [CoInterpreterMT])!
Item was removed:
- ----- Method: CogVMSimulator>>primitiveProcessBindToThreadId (in category 'multi-threading simulation switch') -----
- primitiveProcessBindToThreadId
- "This method includes or excludes CoInterpreterMT methods as required.
- Auto-generated by CogVMSimulator>>ensureMultiThreadingOverridesAreUpToDate"
-
- ^self perform: #primitiveProcessBindToThreadId
- withArguments: {}
- inSuperclass: (cogThreadManager ifNil: [CoInterpreterPrimitives] ifNotNil: [CoInterpreterMT])!
Item was added:
+ ----- Method: InterpreterPrimitives>>primitiveProcessBindToThreadAffinity (in category 'process primitives') -----
+ primitiveProcessBindToThreadAffinity
+ "Simulation only. Fail."
+ <doNotGenerate>
+ self primitiveFail!
Item was removed:
- ----- Method: InterpreterPrimitives>>primitiveProcessBindToThreadId (in category 'process primitives') -----
- primitiveProcessBindToThreadId
- "Simulation only. Fail."
- <doNotGenerate>
- self primitiveFail!
Item was changed:
----- Method: StackInterpreter>>removeProcess:fromList: (in category 'process primitive support') -----
removeProcess: aProcess fromList: aList
"Attempt to remove a process from a linked list. Answer if the attempt succeeded."
| firstLink lastLink nextLink tempLink |
self deny: (objectMemory isForwarded: aProcess).
self deny: (objectMemory isForwarded: aList).
"any process on the list could have been becomed, so use a read barrier..."
firstLink := objectMemory followField: FirstLinkIndex ofObject: aList.
lastLink := objectMemory followField: LastLinkIndex ofObject: aList.
aProcess = firstLink
ifTrue:
[nextLink := objectMemory followField: NextLinkIndex ofObject: aProcess.
objectMemory storePointer: FirstLinkIndex ofObject: aList withValue: nextLink.
aProcess = lastLink ifTrue:
[objectMemory storePointerUnchecked: LastLinkIndex ofObject: aList withValue: objectMemory nilObject]]
ifFalse:
[tempLink := firstLink.
["fail if any link doesn't look like a process..."
((objectMemory isPointers: tempLink)
and: [(objectMemory numSlotsOf: tempLink) > MyListIndex]) ifFalse:
[^false].
nextLink := objectMemory followField: NextLinkIndex ofObject: tempLink.
nextLink = aProcess] whileFalse:
[tempLink := nextLink].
nextLink := objectMemory fetchPointer: NextLinkIndex ofObject: aProcess.
objectMemory storePointer: NextLinkIndex ofObject: tempLink withValue: nextLink.
aProcess = lastLink ifTrue:
[objectMemory storePointer: LastLinkIndex ofObject: aList withValue: tempLink]].
objectMemory storePointerUnchecked: NextLinkIndex ofObject: aProcess withValue: objectMemory nilObject.
+ objectMemory storePointerUnchecked: MyListIndex ofObject: aProcess withValue: objectMemory nilObject.
^true!
Item was changed:
----- Method: StackInterpreterPrimitives>>primitiveSuspend (in category 'process primitives') -----
primitiveSuspend
"Primitive #88. Suspend the receiver, aProcess, such that it can be executed again
by sending #resume. If the given process is not the active process, take it off
its corresponding list. The primitive returns the list the receiver was previously on.
c.f. primitiveSuspendBackingUpV1,#568 & primitiveSuspendBackingUpV2,#578"
| process myList myContext ok |
process := self stackTop.
process = self activeProcess ifTrue:
[self stackTopPut: objectMemory nilObject.
^self transferTo: self wakeHighestPriority].
myList := objectMemory fetchPointer: MyListIndex ofObject: process.
myContext := objectMemory fetchPointer: SuspendedContextIndex ofObject: process.
((objectMemory isPointers: myList)
and: [(objectMemory numSlotsOf: myList) > LastLinkIndex
and: [(objectMemory isContext: myContext)
and: [self isResumableContext: myContext]]]) ifFalse:
[^self primitiveFailFor: PrimErrBadReceiver].
ok := self removeProcess: process fromList: myList.
ok ifFalse:
[^self primitiveFailFor: PrimErrOperationFailed].
- objectMemory storePointerUnchecked: MyListIndex ofObject: process withValue: objectMemory nilObject.
self stackTopPut: myList!
Item was changed:
----- Method: StackInterpreterPrimitives>>primitiveSuspendBackingUpV1 (in category 'process primitives') -----
primitiveSuspendBackingUpV1
"Primitive #568. Suspend the receiver, aProcess, such that it can be executed again
by sending #resume. If the given process is not the active process, take it off
its corresponding list. If the list was not its run queue assume it was on some
condition variable (Semaphore, Mutex) and back up its pc to the send that
invoked the wait state the process entered. Hence when the process resumes
it will reenter the wait state. Answer the list the receiver was previously on,
unless it was the activeProcess, in which case answer nil.
c.f. primitiveSuspend,#88 & primitiveSuspendBackingUpV2,#578"
| process myList myContext ok |
process := self stackTop.
process = self activeProcess ifTrue:
[self stackTopPut: objectMemory nilObject.
^self transferTo: self wakeHighestPriority].
myList := objectMemory fetchPointer: MyListIndex ofObject: process.
myContext := objectMemory fetchPointer: SuspendedContextIndex ofObject: process.
((objectMemory isPointers: myList)
and: [(objectMemory numSlotsOf: myList) > LastLinkIndex
and: [(objectMemory isContext: myContext)
and: [self isResumableContext: myContext]]]) ifFalse:
[^self primitiveFailFor: PrimErrBadReceiver].
ok := self removeProcess: process fromList: myList.
ok ifFalse:
[^self primitiveFailFor: PrimErrOperationFailed].
- objectMemory storePointerUnchecked: MyListIndex ofObject: process withValue: objectMemory nilObject.
(objectMemory fetchClassTagOfNonImm: myList) ~= classLinkedListClassTag ifTrue:
[self backupContext: myContext toBlockingSendTo: myList].
self stackTopPut: myList!
Item was changed:
----- Method: StackInterpreterPrimitives>>primitiveSuspendBackingUpV2 (in category 'process primitives') -----
primitiveSuspendBackingUpV2
"Primitive #578. Suspend the receiver, aProcess, such that it can be executed again
by sending #resume. If the given process is not the active process, take it off
its corresponding list. If the list was not its run queue assume it was on some
condition variable (Semaphore, Mutex) and back up its pc to the send that
invoked the wait state the process entered. Hence when the process resumes
it will reenter the wait state. Answer the list the receiver was previously on iff
it was not active and not blocked, otherwise answer nil.
c.f. primitiveSuspend,#88 & primitiveSuspendBackingUpV1,#568,
which always answer the list the process was on, even if blocked."
<export: true>
| process myList myContext ok |
process := self stackTop.
process = self activeProcess ifTrue:
[self stackTopPut: objectMemory nilObject.
^self transferTo: self wakeHighestPriority].
myList := objectMemory fetchPointer: MyListIndex ofObject: process.
myContext := objectMemory fetchPointer: SuspendedContextIndex ofObject: process.
((objectMemory isPointers: myList)
and: [(objectMemory numSlotsOf: myList) > LastLinkIndex
and: [(objectMemory isContext: myContext)
and: [self isResumableContext: myContext]]]) ifFalse:
[^self primitiveFailFor: PrimErrBadReceiver].
ok := self removeProcess: process fromList: myList.
ok ifFalse:
[^self primitiveFailFor: PrimErrOperationFailed].
- objectMemory storePointerUnchecked: MyListIndex ofObject: process withValue: objectMemory nilObject.
(objectMemory fetchClassTagOfNonImm: myList) ~= classLinkedListClassTag
ifTrue:
[self backupContext: myContext toBlockingSendTo: myList.
self stackTopPut: objectMemory nilObject]
ifFalse:
[self stackTopPut: myList]!
Leon Matthes uploaded a new version of VMMaker to project VM Maker:
http://source.squeak.org/VMMaker/VMMaker.threaded-LM.3345.mcz
==================== Summary ====================
Name: VMMaker.threaded-LM.3345
Author: LM
Time: 14 November 2023, 1:59:11.261792 pm
UUID: b6293fcf-0133-4cd7-b5de-d73eaa4b99b2
Ancestors: VMMaker.threaded-LM.3344
No longer use the list of awol processes within CogVMThread.
awol processes are now stored in the ProcessInExternalCodeTag.
Also do some refactorings to get threadAffinity and temporaryThreadAffinity in line.
=============== Diff against VMMaker.threaded-LM.3344 ===============
Item was changed:
----- Method: CoInterpreterMT class>>initializePrimitiveTable (in category 'initialization') -----
initializePrimitiveTable
super initializePrimitiveTable.
PrimNumberRelinquishProcessor := 230.
COGMTVM ifTrue:
[(227 to: 229) do:
[:pidx| self assert: (PrimitiveTable at: pidx + 1) = #primitiveFail].
PrimitiveTable
at: 227 + 1 put: #primitiveVMCurrentThreadId;
at: 228 + 1 put: #primitiveProcessBoundThreadId;
+ at: 229 + 1 put: #primitiveProcessBindToThreadAffinity]!
- at: 229 + 1 put: #primitiveProcessBindToThreadId]!
Item was removed:
- ----- Method: CoInterpreterMT>>affinedThreadId: (in category 'process primitive support') -----
- affinedThreadId: threadIdField
- "Answer the threadId of the thread threadIdField is temporarily bound to, or 0 if none."
- ^(objectMemory isIntegerObject: threadIdField)
- ifTrue: [(objectMemory integerValueOf: threadIdField) anyMask: 1 << ThreadIdShift - 1]
- ifFalse: [0]!
Item was added:
+ ----- Method: CoInterpreterMT>>bindProcess:toAffinity: (in category 'process primitive support') -----
+ bindProcess: aProcess toAffinity: newAffinity
+ "Change a Process's thread binding and answer 0, otherwise answer a suitable error code.
+ Cases:
+ process is unbound & unaffined
+ affinity 0 nothing to do
+ affinity non-zero ensure thread and bind
+ process is affined (temporarily bound to a thread for the duration of a surrender of ownership)
+ affinity = affined index nothing to do
+ affinity = 0 nothing to do
+ affinity ~= 0 && affinity ~= affined index fail
+ process is bound (permanently bound to a thread)
+ affinity = bound index nothing to do
+ affinity ~= bound index set bound index"
+ | threadIdField currentAffinity temporaryAffinity |
+ processHasThreadAffinity ifFalse:
+ [^PrimErrUnsupported].
+
+ threadIdField := self threadAffinityFieldOf: aProcess.
+ currentAffinity := self threadAffinityOfThreadID: threadIdField.
+ temporaryAffinity := self temporaryAffinedThreadId: threadIdField.
+
+ "If aProcess is affined (temporarily bound to) a thread then the operation can only
+ succeed if the newId is the same as that aProcess is affined to, or is zero (is unbinding)."
+ (self isTemporaryAffinedThreadId: threadIdField) ifTrue:
+ [(newAffinity = 0 or: [newAffinity = temporaryAffinity]) ifFalse:
+ [^PrimErrInappropriate]].
+
+ currentAffinity > 0 ifTrue:
+ [(self startThreadForThreadIndex: currentAffinity) ifFalse:
+ [^PrimErrLimitExceeded]].
+
+ self setThreadIdFieldOfProcess: aProcess toAffinity: newAffinity andTemporaryAffinity: temporaryAffinity.
+ ^nil!
Item was removed:
- ----- Method: CoInterpreterMT>>bindProcess:toId: (in category 'process primitive support') -----
- bindProcess: aProcess toId: newId
- "Change a Process's thread binding and answer 0, otherwise answer a suitable error code.
- Cases:
- process is unbound & unaffined
- affinity 0 nothing to do
- affinity non-zero ensure thread and bind
- process is affined (temporarily bound to a thread for the duration of a surrender of ownership)
- affinity = affined index nothing to do
- affinity = 0 nothing to do
- affinity ~= 0 && affinity ~= affined index fail
- process is bound (permanently bound to a thread)
- affinity = bound index nothing to do
- affinity ~= bound index set bound index"
- | threadIdField ownerIndex affinedId |
- processHasThreadAffinity ifFalse:
- [^PrimErrUnsupported].
-
- threadIdField := self threadAffinityFieldOf: aProcess.
- ownerIndex := self ownerIndexOfThreadId: threadIdField.
-
- "If aProcess is affined (temporarily bound to) a thread then the operation can only
- succeed if the newId is the same as that aProcess is affined to, or is zero (is unbinding)."
- (self isAffinedThreadId: threadIdField) ifTrue:
- [affinedId := self affinedThreadId: threadIdField.
- (newId = 0
- or: [newId = affinedId]) ifTrue:
- [self setThreadIdFieldOfProcess: aProcess to: newId << ThreadIdShift + affinedId.
- ^nil].
- ^PrimErrInappropriate].
-
- ownerIndex > 0 ifTrue:
- [(self startThreadForThreadIndex: ownerIndex) ifFalse:
- [^PrimErrLimitExceeded]].
-
- self setThreadIdFieldOfProcess: aProcess to: newId << ThreadIdShift.
- ^nil!
Item was removed:
- ----- Method: CoInterpreterMT>>isAffinedProcess: (in category 'process primitive support') -----
- isAffinedProcess: aProcess
- ^self isAffinedThreadId: (self threadAffinityFieldValueOf: aProcess)!
Item was removed:
- ----- Method: CoInterpreterMT>>isAffinedThreadId: (in category 'process primitive support') -----
- isAffinedThreadId: threadIdField
- "Answer if the threadId has the bits set indicating the thread it is temporarily bound to."
- ^(self affinedThreadId: threadIdField) ~= 0!
Item was added:
+ ----- Method: CoInterpreterMT>>isTemporaryAffinedProcess: (in category 'process primitive support') -----
+ isTemporaryAffinedProcess: aProcess
+ ^self isTemporaryAffinedThreadId: (self threadAffinityFieldValueOf: aProcess)!
Item was added:
+ ----- Method: CoInterpreterMT>>isTemporaryAffinedThreadId: (in category 'process primitive support') -----
+ isTemporaryAffinedThreadId: threadIdField
+ "Answer if the threadId has the bits set indicating the thread it is temporarily bound to."
+ ^(self temporaryAffinedThreadId: threadIdField) ~= 0!
Item was removed:
- ----- Method: CoInterpreterMT>>ownerIndexOfThreadId: (in category 'process primitive support') -----
- ownerIndexOfThreadId: threadId
- ^(objectMemory isIntegerObject: threadId)
- ifTrue: ["We need a signed shift here (>>>), as otherwise we lose the sign of the threadId."
- (objectMemory integerValueOf: threadId) >>> ThreadIdShift]
- ifFalse: [0]!
Item was added:
+ ----- Method: CoInterpreterMT>>popProcessWithTemporaryAffinity:fromList: (in category 'process primitive support') -----
+ popProcessWithTemporaryAffinity: anAffinity fromList: aList
+ "Find the first process from the list that is temporarily affined to the given affinity.
+ Remove this process from the list and return it.
+ This is used by the preempt/restore flow to find the process that is to be restored."
+ | firstLink lastLink nextLink tempLink theProcess |
+ self assert: (anAffinity ~= 0).
+ self deny: (objectMemory isForwarded: aList).
+ "any process on the list could have been becomed, so use a read barrier..."
+ firstLink := objectMemory followField: FirstLinkIndex ofObject: aList.
+ lastLink := objectMemory followField: LastLinkIndex ofObject: aList.
+ "fail if any link doesn't look like a process..."
+ ((objectMemory isPointers: firstLink)
+ and: [(objectMemory numSlotsOf: firstLink) > MyListIndex]) ifFalse:
+ [^ objectMemory nilObject].
+
+ (firstLink ~= objectMemory nilObject and: [(self temporaryAffinityOfProcess: firstLink) = anAffinity])
+ ifTrue:
+ [theProcess := firstLink.
+ nextLink := objectMemory followField: NextLinkIndex ofObject: firstLink.
+ objectMemory storePointer: FirstLinkIndex ofObject: aList withValue: nextLink.
+ firstLink = lastLink ifTrue:
+ [objectMemory storePointerUnchecked: LastLinkIndex ofObject: aList withValue: objectMemory nilObject]]
+ ifFalse:
+ [tempLink := firstLink.
+ [
+ nextLink := objectMemory followField: NextLinkIndex ofObject: tempLink.
+ "fail if any link doesn't look like a process..."
+ ((objectMemory isPointers: nextLink)
+ and: [(objectMemory numSlotsOf: nextLink) > MyListIndex]) ifFalse:
+ [^ objectMemory nilObject].
+ (self temporaryAffinityOfProcess: nextLink) = anAffinity]
+ whileFalse: [tempLink := nextLink].
+
+ theProcess := nextLink.
+ nextLink := objectMemory fetchPointer: NextLinkIndex ofObject: nextLink.
+ objectMemory storePointer: NextLinkIndex ofObject: tempLink withValue: nextLink.
+ theProcess = lastLink ifTrue:
+ [objectMemory storePointer: LastLinkIndex ofObject: aList withValue: tempLink]].
+ objectMemory storePointerUnchecked: NextLinkIndex ofObject: theProcess withValue: objectMemory nilObject.
+ ^ theProcess!
Item was changed:
----- Method: CoInterpreterMT>>preemptDisowningThread (in category 'vm scheduling') -----
preemptDisowningThread
"Set the relevant state for disowningVMThread so that it can resume after
being preempted and set disowningVMThread to nil to indicate preemption.
N.B. This should only be sent from checkPreemptionOfDisowningThread.
There are essentially four things to do.
a) save the VM's notion of the current C stack pointers; these are pointers
into a thread's stack and must be saved and restored in thread switch.
b) save the VM's notion of the current Smalltalk execution point. This is
simply the suspend half of a process switch that saves the current context
in the current process.
c) add the process to the thread's set of AWOL processes so that the scheduler
won't try to run the process while the thread has disowned the VM.
d) save the in-primitive VM state, newMethod and argumentCount
ownVM: will restore the VM context as of disownVM: from the above when it
finds it has been preempted."
| activeProc activeContext preemptedThread |
<var: #preemptedThread type: #'CogVMThread *'>
<inline: false>
self assert: disowningVMThread notNil.
self assert: (disowningVMThread vmThreadState = CTMUnavailable
or: [disowningVMThread vmThreadState = CTMWantingOwnership]).
self assertCStackPointersBelongToDisowningThread.
cogit recordEventTrace ifTrue:
[self recordTrace: TracePreemptDisowningThread
thing: (objectMemory integerObjectOf: disowningVMThread index)
source: 0].
disowningVMThread cStackPointer: CStackPointer.
disowningVMThread cFramePointer: CFramePointer.
activeProc := self activeProcess.
- "To make sure the process isn't garbage collected, add the Process to the list of processes that are in external code.
- This should also be helpful if we want to fail any processes that are AWOL when restarting an image."
- self addFirstLink: activeProc toList: (objectMemory splObj: ProcessInExternalCodeTag).
activeContext := self ensureFrameIsMarried: framePointer SP: stackPointer.
objectMemory
storePointer: SuspendedContextIndex
ofObject: activeProc
withValue: activeContext.
"The instructionPointer must be pushed because the convention for inactive stack pages is that the
instructionPointer is top of stack. We need to know if this primitive is called from machine code
because the invariant that the return pc of an interpreter callee calling a machine code caller is
ceReturnToInterpreterPC must be maintained."
self push: instructionPointer.
self externalWriteBackHeadFramePointers.
"Since pushing the awol process may realloc disowningVMThread we need to reassign.
But since we're going to nil disowningVMThread anyway we can assign to a local."
- cogThreadManager pushAWOLProcess: activeProc on: disowningVMThread.
preemptedThread := disowningVMThread.
disowningVMThread := nil.
+
+ "Store the process in the ProcessInExternalCodeTag special object (a LinkedList).
+ This ensures:
+ - The process isn't garbage collected
+ - If the process is moved due to GC, we can still find it
+ - The process is available from inside the image
+ On restore we can use the temporaryThreadAffinity to find the last process that was disowned from the preempted thread.
+ This therefore creates a LIFO stack for each thread which are all interleaved in this one list."
+ self assert: (self isTemporaryAffinedProcess: activeProc) not.
+ self setTemporaryThreadAffinityOfProcess: activeProc to: preemptedThread index.
+ self addFirstLink: activeProc toList: (objectMemory splObj: ProcessInExternalCodeTag).
+
- (self threadAffinityOfProcess: activeProc) = 0 ifTrue:
- [self setTemporaryThreadAffinityOfProcess: activeProc to: preemptedThread index bind: false].
preemptedThread
newMethodOrNull: newMethod;
argumentCount: argumentCount;
inMachineCode: instructionPointer <= objectMemory startOfMemory!
Item was added:
+ ----- Method: CoInterpreterMT>>primitiveProcessBindToThreadAffinity (in category 'process primitives') -----
+ primitiveProcessBindToThreadAffinity
+ "Attempt to bind the receiver to the thread affinity of the argument or nil, where the receiver is a Process.
+ The thread affinity may be an integer where:
+ 0 - means no thread affinity, the process is free to run on any thread.
+ > 0 - positive values mean the process has to run on the thread with this specific index.
+ < 0 - negative values mean the process may run on on any thread **APART** from the thread
+ with the absolute value of the index.
+
+ Usually values of 1, -1 and 0 are used.
+ Thread number 1 is the thread the VM started with. On some OSes this thread has special priviliges.
+ I.e. on macOS only thread 1 can make draw calls.
+ Therefore it is mostly important whether a thread must run on thread 1, must **not** run on thread 1
+ or whether it doesn't care.
+
+ If successful the VM will ensure that there is at least one compatible thread active."
+ | aProcess affinity waitingPriority activePriority |
+ <export: true>
+ self cCode: [] inSmalltalk: [cogThreadManager isNil ifTrue: [^self primitiveFail]].
+ processHasThreadAffinity ifFalse:
+ [^self primitiveFailFor: PrimErrUnsupported].
+ affinity := self stackTop.
+ aProcess := self stackValue: 1.
+ ((affinity = objectMemory nilObject or: [(objectMemory isIntegerObject: affinity)
+ and: [affinity ~= (objectMemory integerObjectOf: 0)]])
+ and: [(objectMemory isPointers: aProcess)
+ and: [(objectMemory slotSizeOf: aProcess) >= (ThreadIdIndex + 1)]]) ifFalse:
+ [^self primitiveFailFor: PrimErrBadArgument].
+ affinity := affinity = objectMemory nilObject ifTrue: [0] ifFalse: [objectMemory integerValueOf: affinity].
+ affinity abs >= cogThreadManager maxNumThreads ifTrue:
+ [^self primitiveFailFor: PrimErrLimitExceeded].
+ (self bindProcess: aProcess toAffinity: affinity) ifNotNil:
+ [:ec| ^self primitiveFailFor: ec].
+ self methodReturnReceiver.
+
+ waitingPriority := self getMaxWaitingPriority.
+ activePriority := self quickFetchInteger: PriorityIndex ofObject: aProcess.
+ affinity := self threadAffinityOfProcess: aProcess.
+ (aProcess = self activeProcess
+ and: [(activeProcessAffined := affinity ~= 0)
+ and: [(cogThreadManager vmOwnerIsCompatibleWith: affinity) not]]) ifTrue:
+ [activePriority < waitingPriority ifTrue:
+ [self reduceWaitingPriorityFrom: waitingPriority to: activePriority "TODO: Check if this is correct?"].
+ self threadSwitchIfNecessary: aProcess from: CSThreadBind]!
Item was removed:
- ----- Method: CoInterpreterMT>>primitiveProcessBindToThreadId (in category 'process primitives') -----
- primitiveProcessBindToThreadId
- "Attempt to bind the receiver to the thread with the id of the argument or nil, where the receiver is a Process.
- If successful the VM will ensure that there are at least id many threads active."
- | aProcess id waitingPriority activePriority |
- <export: true>
- self cCode: [] inSmalltalk: [cogThreadManager isNil ifTrue: [^self primitiveFail]].
- processHasThreadAffinity ifFalse:
- [^self primitiveFailFor: PrimErrUnsupported].
- id := self stackTop.
- aProcess := self stackValue: 1.
- ((id = objectMemory nilObject or: [(objectMemory isIntegerObject: id)
- and: [id ~= (objectMemory integerObjectOf: 0)]])
- and: [(objectMemory isPointers: aProcess)
- and: [(objectMemory slotSizeOf: aProcess) >= (ThreadIdIndex + 1)]]) ifFalse:
- [^self primitiveFailFor: PrimErrBadArgument].
- id := id = objectMemory nilObject ifTrue: [0] ifFalse: [objectMemory integerValueOf: id].
- id abs >= cogThreadManager maxNumThreads ifTrue:
- [^self primitiveFailFor: PrimErrLimitExceeded].
- (self bindProcess: aProcess toId: id) ifNotNil:
- [:ec| ^self primitiveFailFor: ec].
- self methodReturnReceiver.
-
- waitingPriority := self getMaxWaitingPriority.
- activePriority := self quickFetchInteger: PriorityIndex ofObject: aProcess.
- id := self threadAffinityOfProcess: aProcess.
- (aProcess = self activeProcess
- and: [(activeProcessAffined := id ~= 0)
- and: [(cogThreadManager vmOwnerIsCompatibleWith: id) not]]) ifTrue:
- [activePriority < waitingPriority ifTrue:
- [self reduceWaitingPriorityFrom: waitingPriority to: activePriority "TODO: Check if this is correct?"].
- self threadSwitchIfNecessary: aProcess from: CSThreadBind]!
Item was added:
+ ----- Method: CoInterpreterMT>>removeFirstProcessWithTemporaryAffinity:fromList: (in category 'process primitive support') -----
+ removeFirstProcessWithTemporaryAffinity: anAffinity fromList: aList
+
+ "Find the first process from the list that is temporarily affined to the given affinity.
+ Remove this process from the list and return it.
+ This is used by the preempt/restore flow to find the process that is to be restored."
+ | firstLink lastLink nextLink tempLink theProcess |
+ self assert: (anAffinity ~= 0).
+ self deny: (objectMemory isForwarded: aList).
+ "any process on the list could have been becomed, so use a read barrier..."
+ firstLink := objectMemory followField: FirstLinkIndex ofObject: aList.
+ lastLink := objectMemory followField: LastLinkIndex ofObject: aList.
+ (firstLink ~= objectMemory nilObject and: [(self temporaryAffinityOfProcess: firstLink) = anAffinity])
+ ifTrue:
+ [theProcess := firstLink.
+ nextLink := objectMemory followField: NextLinkIndex ofObject: firstLink.
+ objectMemory storePointer: FirstLinkIndex ofObject: aList withValue: nextLink.
+ firstLink = lastLink ifTrue:
+ [objectMemory storePointerUnchecked: LastLinkIndex ofObject: aList withValue: objectMemory nilObject]]
+ ifFalse:
+ [tempLink := firstLink.
+ ["fail if any link doesn't look like a process..."
+ ((objectMemory isPointers: tempLink)
+ and: [(objectMemory numSlotsOf: tempLink) > MyListIndex]) ifFalse:
+ [^false].
+ nextLink := objectMemory followField: NextLinkIndex ofObject: tempLink.
+ nextLink ~= objectMemory nilObject and: [(self temporaryAffinityOfProcess: nextLink) = anAffinity]]
+ whileFalse: [tempLink := nextLink].
+
+ nextLink = objectMemory nilObject ifTrue: [^ false].
+
+ theProcess := nextLink.
+ nextLink := objectMemory fetchPointer: NextLinkIndex ofObject: nextLink.
+ objectMemory storePointer: NextLinkIndex ofObject: tempLink withValue: nextLink.
+ theProcess = lastLink ifTrue:
+ [objectMemory storePointer: LastLinkIndex ofObject: aList withValue: tempLink]].
+ objectMemory storePointerUnchecked: NextLinkIndex ofObject: theProcess withValue: objectMemory nilObject.
+ ^true!
Item was changed:
----- Method: CoInterpreterMT>>restoreVMStateFor:andFlags: (in category 'vm scheduling') -----
restoreVMStateFor: vmThread andFlags: flags
"We've been preempted; we must restore state and update the threadId
in our process, and may have to put the active process to sleep."
+ | sched activeProc myProc |
- | sched activeProc myProc removedFromList |
sched := self schedulerPointer.
activeProc := objectMemory fetchPointer: ActiveProcessIndex ofObject: sched.
(flags anyMask: OwnVMForeignThreadFlag)
ifTrue:
[self assert: foreignCallbackProcessSlot == ForeignCallbackProcess.
myProc := objectMemory splObj: foreignCallbackProcessSlot.
self assert: myProc ~= objectMemory nilObject.
objectMemory splObj: foreignCallbackProcessSlot put: objectMemory nilObject]
+ ifFalse: [myProc := self popProcessWithTemporaryAffinity: vmThread index fromList: (objectMemory splObj: ProcessInExternalCodeTag)].
+ self assert: (myProc ~= objectMemory nilObject and: [activeProc ~= myProc]).
- ifFalse: [myProc := cogThreadManager popAWOLProcess: vmThread].
- self assert: activeProc ~= myProc.
(activeProc ~= objectMemory nilObject
and: [(objectMemory fetchPointer: MyListIndex ofObject: activeProc) = objectMemory nilObject]) ifTrue:
[self putToSleep: activeProc yieldingIf: preemptionYields].
- "Make sure to remove the Process from the list of processes that are in external code, such that it may be GCed in future."
- self assert: (objectMemory fetchPointer: MyListIndex ofObject: myProc) = (objectMemory splObj: ProcessInExternalCodeTag).
- removedFromList := self removeProcess: myProc fromList: (objectMemory splObj: ProcessInExternalCodeTag).
- self assert: removedFromList. "We shouldn't put code that does important things into an assert, so save result and check that."
objectMemory
storePointerUnchecked: MyListIndex ofObject: myProc withValue: objectMemory nilObject;
storePointer: ActiveProcessIndex ofObject: sched withValue: myProc.
-
"Only unaffine if the process was affined at this level and did not become bound in the interim."
((flags anyMask: ProcessUnaffinedOnDisown)
and: [(self isBoundProcess: myProc) not]) ifTrue:
+ [self setTemporaryThreadAffinityOfProcess: myProc to: 0].
- [self setTemporaryThreadAffinityOfProcess: myProc to: 0 bind: false].
self initPrimCall.
self cCode:
[self externalSetStackPageAndPointersForSuspendedContextOfProcess: myProc]
inSmalltalk:
["Bypass the no-offset stack depth check in the simulator's externalSetStackPageAndPointersForSuspendedContextOfProcess:"
super externalSetStackPageAndPointersForSuspendedContextOfProcess: myProc.
"We're in ownVM:, hence in a primitive, hence need to include the argument count"
(self isMachineCodeFrame: framePointer) ifTrue:
[self maybeCheckStackDepth: vmThread argumentCount
sp: stackPointer
pc: instructionPointer]].
"If this primitive is called from machine code maintain the invariant that the return pc
of an interpreter callee calling a machine code caller is ceReturnToInterpreterPC."
(vmThread inMachineCode
and: [instructionPointer >= objectMemory startOfMemory]) ifTrue:
[self iframeSavedIP: framePointer put: instructionPointer.
instructionPointer := cogit ceReturnToInterpreterPC].
newMethod := vmThread newMethodOrNull.
argumentCount := vmThread argumentCount.
vmThread newMethodOrNull: nil.
self cCode: '' inSmalltalk:
[| range |
range := self cStackRangeForThreadIndex: vmThread index.
self assert: ((range includes: vmThread cStackPointer) and: [range includes: vmThread cFramePointer])].
self setCFramePointer: vmThread cFramePointer setCStackPointer: vmThread cStackPointer.
self assert: newMethod notNil
!
Item was added:
+ ----- Method: CoInterpreterMT>>setTemporaryThreadAffinityOfProcess:to: (in category 'process primitive support') -----
+ setTemporaryThreadAffinityOfProcess: aProcess to: anIndex
+ "When a thread is disowned for threading, it will temporarily affine the process to itself.
+ Therefore we can make sure no one else accidentally tries to re-bind the process whilst it is AWOL."
+ | threadAffinity |
+ threadAffinity := self threadAffinityOfProcess: aProcess.
+ self setThreadIdFieldOfProcess: aProcess toAffinity: threadAffinity andTemporaryAffinity: anIndex.!
Item was removed:
- ----- Method: CoInterpreterMT>>setTemporaryThreadAffinityOfProcess:to:bind: (in category 'process primitive support') -----
- setTemporaryThreadAffinityOfProcess: aProcess to: anIndex bind: bind
- | threadId |
- threadId := anIndex = 0
- ifTrue: [objectMemory nilObject]
- ifFalse: [objectMemory integerObjectOf: (anIndex << 1) + (bind ifTrue: [1] ifFalse: [0])].
- objectMemory storePointerUnchecked: ThreadIdIndex ofObject: aProcess withValue: threadId!
Item was removed:
- ----- Method: CoInterpreterMT>>setThreadAffinityOfProcess:to:bind: (in category 'process primitive support') -----
- setThreadAffinityOfProcess: aProcess to: anIndex bind: bind
- | threadId |
- threadId := anIndex = 0
- ifTrue: [objectMemory nilObject]
- ifFalse: [objectMemory integerObjectOf: (anIndex << 1) + (bind ifTrue: [1] ifFalse: [0])].
- objectMemory storePointerUnchecked: ThreadIdIndex ofObject: aProcess withValue: threadId!
Item was removed:
- ----- Method: CoInterpreterMT>>setThreadIdFieldOfProcess:to: (in category 'process primitive support') -----
- setThreadIdFieldOfProcess: aProcess to: threadIdField
- | threadIdSlot |
- threadIdSlot := threadIdField = 0
- ifTrue: [objectMemory nilObject]
- ifFalse: [objectMemory integerObjectOf: threadIdField].
- objectMemory storePointerUnchecked: ThreadIdIndex ofObject: aProcess withValue: threadIdSlot!
Item was added:
+ ----- Method: CoInterpreterMT>>setThreadIdFieldOfProcess:toAffinity:andTemporaryAffinity: (in category 'process primitive support') -----
+ setThreadIdFieldOfProcess: aProcess toAffinity: threadAffinity andTemporaryAffinity: anIndex
+ | threadIdSlot bits |
+ self assert: anIndex >= 0.
+ self assert: anIndex <= cogThreadManager maxNumThreads.
+ self assert: threadAffinity >= cogThreadManager maxNumThreads negated.
+ self assert: threadAffinity <= cogThreadManager maxNumThreads.
+
+ anIndex > 0 ifTrue: [self assert: (cogThreadManager threadIndex: anIndex isCompatibleWith: threadAffinity)].
+
+ bits := threadAffinity << ThreadIdShift + anIndex.
+
+ threadIdSlot := bits = 0
+ ifTrue: [objectMemory nilObject]
+ ifFalse: [objectMemory integerObjectOf: bits].
+ objectMemory storePointerUnchecked: ThreadIdIndex ofObject: aProcess withValue: threadIdSlot!
Item was added:
+ ----- Method: CoInterpreterMT>>temporaryAffinedThreadId: (in category 'process primitive support') -----
+ temporaryAffinedThreadId: threadIdField
+ "Answer the threadId of the thread threadIdField is temporarily bound to, or 0 if none."
+ ^(objectMemory isIntegerObject: threadIdField)
+ ifTrue: [(objectMemory integerValueOf: threadIdField) bitAnd: 1 << ThreadIdShift - 1]
+ ifFalse: [0]!
Item was added:
+ ----- Method: CoInterpreterMT>>temporaryAffinityOfProcess: (in category 'process primitive support') -----
+ temporaryAffinityOfProcess: aProcess
+ "Answer the threadId of the thread threadIdField is temporarily bound to, or 0 if none."
+ ^ self temporaryAffinedThreadId: (self threadAffinityFieldOf: aProcess)!
Item was changed:
----- Method: CoInterpreterMT>>threadAffinityOfProcess: (in category 'process primitive support') -----
threadAffinityOfProcess: aProcess
+ ^self threadAffinityOfThreadID: (self threadAffinityFieldOf: aProcess)!
- ^self ownerIndexOfThreadId: (self threadAffinityFieldOf: aProcess)!
Item was added:
+ ----- Method: CoInterpreterMT>>threadAffinityOfThreadID: (in category 'process primitive support') -----
+ threadAffinityOfThreadID: threadId
+ ^(objectMemory isIntegerObject: threadId)
+ ifTrue: ["We need a signed shift here (>>>), as otherwise we lose the sign of the threadId."
+ (objectMemory integerValueOf: threadId) >>> ThreadIdShift]
+ ifFalse: [0]!
Item was changed:
----- Method: CoInterpreterPrimitives>>primitiveSuspend (in category 'process primitives') -----
primitiveSuspend
"Primitive #88. Suspend the receiver, aProcess, such that it can be executed again
by sending #resume. If the given process is not the active process, take it off
its corresponding list. The primitive returns the list the receiver was previously on.
c.f. primitiveSuspendBackingUpV1,#568 & primitiveSuspendBackingUpV2,#578"
| process myList myContext ok |
process := self stackTop.
process = self activeProcess ifTrue:
[| inInterpreter |
"We're going to switch process, either to an interpreted frame or a machine
code frame. To know whether to return or enter machine code we have to
know from whence we came. We could have come from the interpreter,
either directly or via a machine code primitive. We could have come from
machine code. The instructionPointer tells us where from:"
self stackTopPut: objectMemory nilObject.
inInterpreter := instructionPointer >= objectMemory startOfMemory.
self transferTo: self wakeHighestPriority from: CSSuspend.
^self forProcessPrimitiveReturnToExecutivePostContextSwitch: inInterpreter].
myList := objectMemory fetchPointer: MyListIndex ofObject: process.
myContext := objectMemory fetchPointer: SuspendedContextIndex ofObject: process.
((objectMemory isPointers: myList)
and: [(objectMemory numSlotsOf: myList) > LastLinkIndex
and: [(objectMemory isContext: myContext)
and: [self isResumableContext: myContext]]]) ifFalse:
[^self primitiveFailFor: PrimErrBadReceiver].
ok := self removeProcess: process fromList: myList.
ok ifFalse:
[^self primitiveFailFor: PrimErrOperationFailed].
- objectMemory storePointerUnchecked: MyListIndex ofObject: process withValue: objectMemory nilObject.
self stackTopPut: myList!
Item was changed:
----- Method: CoInterpreterPrimitives>>primitiveSuspendBackingUpV1 (in category 'process primitives') -----
primitiveSuspendBackingUpV1
"Primitive #568. Suspend the receiver, aProcess, such that it can be executed again
by sending #resume. If the given process is not the active process, take it off
its corresponding list. If the list was not its run queue assume it was on some
condition variable (Semaphore, Mutex) and back up its pc to the send that
invoked the wait state the process entered. Hence when the process resumes
it will reenter the wait state. Answer the list the receiver was previously on,
unless it was the activeProcess, in which case answer nil.
c.f. primitiveSuspend,#88 & primitiveSuspendBackingUpV2,#578"
| process myList myContext ok |
process := self stackTop.
process = self activeProcess ifTrue:
[| inInterpreter |
"We're going to switch process, either to an interpreted frame or a machine
code frame. To know whether to return or enter machine code we have to
know from whence we came. We could have come from the interpreter,
either directly or via a machine code primitive. We could have come from
machine code. The instructionPointer tells us where from:"
self stackTopPut: objectMemory nilObject.
inInterpreter := instructionPointer >= objectMemory startOfMemory.
self transferTo: self wakeHighestPriority from: CSSuspend.
^self forProcessPrimitiveReturnToExecutivePostContextSwitch: inInterpreter].
myList := objectMemory fetchPointer: MyListIndex ofObject: process.
myContext := objectMemory fetchPointer: SuspendedContextIndex ofObject: process.
((objectMemory isPointers: myList)
and: [(objectMemory numSlotsOf: myList) > LastLinkIndex
and: [(objectMemory isContext: myContext)
and: [self isResumableContext: myContext]]]) ifFalse:
[^self primitiveFailFor: PrimErrBadReceiver].
ok := self removeProcess: process fromList: myList.
ok ifFalse:
[^self primitiveFailFor: PrimErrOperationFailed].
- objectMemory storePointerUnchecked: MyListIndex ofObject: process withValue: objectMemory nilObject.
(objectMemory fetchClassTagOfNonImm: myList) ~= classLinkedListClassTag ifTrue:
[self backupContext: myContext toBlockingSendTo: myList].
self stackTopPut: myList!
Item was changed:
----- Method: CoInterpreterPrimitives>>primitiveSuspendBackingUpV2 (in category 'process primitives') -----
primitiveSuspendBackingUpV2
"Primitive #578. Suspend the receiver, aProcess, such that it can be executed again
by sending #resume. If the given process is not the active process, take it off
its corresponding list. If the list was not its run queue assume it was on some
condition variable (Semaphore, Mutex) and back up its pc to the send that
invoked the wait state the process entered. Hence when the process resumes
it will reenter the wait state. Answer the list the receiver was previously on iff
it was not active and not blocked, otherwise answer nil.
c.f. primitiveSuspend,#88 & primitiveSuspendBackingUpV1,#568,
which always answer the list the process was on, even if blocked."
<export: true>
| process myList myContext ok |
process := self stackTop.
process = self activeProcess ifTrue:
[| inInterpreter |
"We're going to switch process, either to an interpreted frame or a machine
code frame. To know whether to return or enter machine code we have to
know from whence we came. We could have come from the interpreter,
either directly or via a machine code primitive. We could have come from
machine code. The instructionPointer tells us where from:"
self stackTopPut: objectMemory nilObject.
inInterpreter := instructionPointer >= objectMemory startOfMemory.
self transferTo: self wakeHighestPriority from: CSSuspend.
^self forProcessPrimitiveReturnToExecutivePostContextSwitch: inInterpreter].
myList := objectMemory fetchPointer: MyListIndex ofObject: process.
myContext := objectMemory fetchPointer: SuspendedContextIndex ofObject: process.
((objectMemory isPointers: myList)
and: [(objectMemory numSlotsOf: myList) > LastLinkIndex
and: [(objectMemory isContext: myContext)
and: [self isResumableContext: myContext]]]) ifFalse:
[^self primitiveFailFor: PrimErrBadReceiver].
ok := self removeProcess: process fromList: myList.
ok ifFalse:
[^self primitiveFailFor: PrimErrOperationFailed].
- objectMemory storePointerUnchecked: MyListIndex ofObject: process withValue: objectMemory nilObject.
(objectMemory fetchClassTagOfNonImm: myList) ~= classLinkedListClassTag
ifTrue:
[self backupContext: myContext toBlockingSendTo: myList.
self stackTopPut: objectMemory nilObject]
ifFalse:
[self stackTopPut: myList]!
Item was removed:
- ----- Method: CogThreadManager>>growAWOLProcessesOf: (in category 'public api') -----
- growAWOLProcessesOf: vmThread
- <var: #vmThread type: #'CogVMThread*'>
- self
- cCode: [vmThread awolProcesses: (self realloc: vmThread awolProcesses _: vmThread awolProcLength + AWOLProcessesIncrement * (self sizeof: #sqInt))]
- inSmalltalk: [vmThread awolProcesses setObject: vmThread awolProcesses object, (Array new: AWOLProcessesIncrement)].
- vmThread awolProcLength: vmThread awolProcLength + AWOLProcessesIncrement.!
Item was removed:
- ----- Method: CogThreadManager>>pushAWOLProcess:on: (in category 'public api') -----
- pushAWOLProcess: awolProcess on: vmThread
- <var: #vmThread type: #'CogVMThread *'>
- self assert: (vmThread awolProcIndex between: 0 and: vmThread awolProcLength).
- vmThread awolProcIndex >= vmThread awolProcLength
- ifTrue: [self growAWOLProcessesOf: vmThread].
-
- self assert: vmThread awolProcLength > vmThread awolProcIndex.
- vmThread awolProcesses at: vmThread awolProcIndex put: awolProcess.
- vmThread awolProcIndex: vmThread awolProcIndex + 1.!
Item was changed:
----- Method: CogThreadManager>>startThreadForThreadIndex: (in category 'scheduling') -----
startThreadForThreadIndex: index
+ self assert: index > 0.
index > numThreads ifTrue:
[(self growThreadInfosToAtLeast: index) ifFalse:
[^false]].
^self startThreadForThreadInfo: (self vmThreadAt: index)!
Item was removed:
- ----- Method: CogVMSimulator>>bindProcess:toId: (in category 'multi-threading simulation switch') -----
- bindProcess: aProcess toId: newId
- "This method includes or excludes CoInterpreterMT methods as required.
- Auto-generated by CogVMSimulator>>ensureMultiThreadingOverridesAreUpToDate"
-
- ^self perform: #bindProcess:toId:
- withArguments: {aProcess. newId}
- inSuperclass: (cogThreadManager ifNil: [CoInterpreterPrimitives] ifNotNil: [CoInterpreterMT])!
Item was removed:
- ----- Method: CogVMSimulator>>isAffinedProcess: (in category 'multi-threading simulation switch') -----
- isAffinedProcess: aProcess
- "This method includes or excludes CoInterpreterMT methods as required.
- Auto-generated by CogVMSimulator>>ensureMultiThreadingOverridesAreUpToDate"
-
- ^self perform: #isAffinedProcess:
- withArguments: {aProcess}
- inSuperclass: (cogThreadManager ifNil: [CoInterpreterPrimitives] ifNotNil: [CoInterpreterMT])!
Item was removed:
- ----- Method: CogVMSimulator>>isAffinedThreadId: (in category 'multi-threading simulation switch') -----
- isAffinedThreadId: threadId
- "This method includes or excludes CoInterpreterMT methods as required.
- Auto-generated by CogVMSimulator>>ensureMultiThreadingOverridesAreUpToDate"
-
- ^self perform: #isAffinedThreadId:
- withArguments: {threadId}
- inSuperclass: (cogThreadManager ifNil: [CoInterpreterPrimitives] ifNotNil: [CoInterpreterMT])!
Item was removed:
- ----- Method: CogVMSimulator>>ownerIndexOfThreadId: (in category 'multi-threading simulation switch') -----
- ownerIndexOfThreadId: threadId
- "This method includes or excludes CoInterpreterMT methods as required.
- Auto-generated by CogVMSimulator>>ensureMultiThreadingOverridesAreUpToDate"
-
- ^self perform: #ownerIndexOfThreadId:
- withArguments: {threadId}
- inSuperclass: (cogThreadManager ifNil: [CoInterpreterPrimitives] ifNotNil: [CoInterpreterMT])!
Item was added:
+ ----- Method: CogVMSimulator>>primitiveProcessBindToThreadAffinity (in category 'multi-threading simulation switch') -----
+ primitiveProcessBindToThreadAffinity
+ "This method includes or excludes CoInterpreterMT methods as required.
+ Auto-generated by CogVMSimulator>>ensureMultiThreadingOverridesAreUpToDate"
+
+ ^self perform: #primitiveProcessBindToThreadAffinity
+ withArguments: {}
+ inSuperclass: (cogThreadManager ifNil: [CoInterpreterPrimitives] ifNotNil: [CoInterpreterMT])!
Item was removed:
- ----- Method: CogVMSimulator>>primitiveProcessBindToThreadId (in category 'multi-threading simulation switch') -----
- primitiveProcessBindToThreadId
- "This method includes or excludes CoInterpreterMT methods as required.
- Auto-generated by CogVMSimulator>>ensureMultiThreadingOverridesAreUpToDate"
-
- ^self perform: #primitiveProcessBindToThreadId
- withArguments: {}
- inSuperclass: (cogThreadManager ifNil: [CoInterpreterPrimitives] ifNotNil: [CoInterpreterMT])!
Item was added:
+ ----- Method: InterpreterPrimitives>>primitiveProcessBindToThreadAffinity (in category 'process primitives') -----
+ primitiveProcessBindToThreadAffinity
+ "Simulation only. Fail."
+ <doNotGenerate>
+ self primitiveFail!
Item was removed:
- ----- Method: InterpreterPrimitives>>primitiveProcessBindToThreadId (in category 'process primitives') -----
- primitiveProcessBindToThreadId
- "Simulation only. Fail."
- <doNotGenerate>
- self primitiveFail!
Item was changed:
----- Method: StackInterpreter>>removeProcess:fromList: (in category 'process primitive support') -----
removeProcess: aProcess fromList: aList
"Attempt to remove a process from a linked list. Answer if the attempt succeeded."
| firstLink lastLink nextLink tempLink |
self deny: (objectMemory isForwarded: aProcess).
self deny: (objectMemory isForwarded: aList).
"any process on the list could have been becomed, so use a read barrier..."
firstLink := objectMemory followField: FirstLinkIndex ofObject: aList.
lastLink := objectMemory followField: LastLinkIndex ofObject: aList.
aProcess = firstLink
ifTrue:
[nextLink := objectMemory followField: NextLinkIndex ofObject: aProcess.
objectMemory storePointer: FirstLinkIndex ofObject: aList withValue: nextLink.
aProcess = lastLink ifTrue:
[objectMemory storePointerUnchecked: LastLinkIndex ofObject: aList withValue: objectMemory nilObject]]
ifFalse:
[tempLink := firstLink.
["fail if any link doesn't look like a process..."
((objectMemory isPointers: tempLink)
and: [(objectMemory numSlotsOf: tempLink) > MyListIndex]) ifFalse:
[^false].
nextLink := objectMemory followField: NextLinkIndex ofObject: tempLink.
nextLink = aProcess] whileFalse:
[tempLink := nextLink].
nextLink := objectMemory fetchPointer: NextLinkIndex ofObject: aProcess.
objectMemory storePointer: NextLinkIndex ofObject: tempLink withValue: nextLink.
aProcess = lastLink ifTrue:
[objectMemory storePointer: LastLinkIndex ofObject: aList withValue: tempLink]].
objectMemory storePointerUnchecked: NextLinkIndex ofObject: aProcess withValue: objectMemory nilObject.
+ objectMemory storePointerUnchecked: MyListIndex ofObject: aProcess withValue: objectMemory nilObject.
^true!
Item was changed:
----- Method: StackInterpreterPrimitives>>primitiveSuspend (in category 'process primitives') -----
primitiveSuspend
"Primitive #88. Suspend the receiver, aProcess, such that it can be executed again
by sending #resume. If the given process is not the active process, take it off
its corresponding list. The primitive returns the list the receiver was previously on.
c.f. primitiveSuspendBackingUpV1,#568 & primitiveSuspendBackingUpV2,#578"
| process myList myContext ok |
process := self stackTop.
process = self activeProcess ifTrue:
[self stackTopPut: objectMemory nilObject.
^self transferTo: self wakeHighestPriority].
myList := objectMemory fetchPointer: MyListIndex ofObject: process.
myContext := objectMemory fetchPointer: SuspendedContextIndex ofObject: process.
((objectMemory isPointers: myList)
and: [(objectMemory numSlotsOf: myList) > LastLinkIndex
and: [(objectMemory isContext: myContext)
and: [self isResumableContext: myContext]]]) ifFalse:
[^self primitiveFailFor: PrimErrBadReceiver].
ok := self removeProcess: process fromList: myList.
ok ifFalse:
[^self primitiveFailFor: PrimErrOperationFailed].
- objectMemory storePointerUnchecked: MyListIndex ofObject: process withValue: objectMemory nilObject.
self stackTopPut: myList!
Item was changed:
----- Method: StackInterpreterPrimitives>>primitiveSuspendBackingUpV1 (in category 'process primitives') -----
primitiveSuspendBackingUpV1
"Primitive #568. Suspend the receiver, aProcess, such that it can be executed again
by sending #resume. If the given process is not the active process, take it off
its corresponding list. If the list was not its run queue assume it was on some
condition variable (Semaphore, Mutex) and back up its pc to the send that
invoked the wait state the process entered. Hence when the process resumes
it will reenter the wait state. Answer the list the receiver was previously on,
unless it was the activeProcess, in which case answer nil.
c.f. primitiveSuspend,#88 & primitiveSuspendBackingUpV2,#578"
| process myList myContext ok |
process := self stackTop.
process = self activeProcess ifTrue:
[self stackTopPut: objectMemory nilObject.
^self transferTo: self wakeHighestPriority].
myList := objectMemory fetchPointer: MyListIndex ofObject: process.
myContext := objectMemory fetchPointer: SuspendedContextIndex ofObject: process.
((objectMemory isPointers: myList)
and: [(objectMemory numSlotsOf: myList) > LastLinkIndex
and: [(objectMemory isContext: myContext)
and: [self isResumableContext: myContext]]]) ifFalse:
[^self primitiveFailFor: PrimErrBadReceiver].
ok := self removeProcess: process fromList: myList.
ok ifFalse:
[^self primitiveFailFor: PrimErrOperationFailed].
- objectMemory storePointerUnchecked: MyListIndex ofObject: process withValue: objectMemory nilObject.
(objectMemory fetchClassTagOfNonImm: myList) ~= classLinkedListClassTag ifTrue:
[self backupContext: myContext toBlockingSendTo: myList].
self stackTopPut: myList!
Item was changed:
----- Method: StackInterpreterPrimitives>>primitiveSuspendBackingUpV2 (in category 'process primitives') -----
primitiveSuspendBackingUpV2
"Primitive #578. Suspend the receiver, aProcess, such that it can be executed again
by sending #resume. If the given process is not the active process, take it off
its corresponding list. If the list was not its run queue assume it was on some
condition variable (Semaphore, Mutex) and back up its pc to the send that
invoked the wait state the process entered. Hence when the process resumes
it will reenter the wait state. Answer the list the receiver was previously on iff
it was not active and not blocked, otherwise answer nil.
c.f. primitiveSuspend,#88 & primitiveSuspendBackingUpV1,#568,
which always answer the list the process was on, even if blocked."
<export: true>
| process myList myContext ok |
process := self stackTop.
process = self activeProcess ifTrue:
[self stackTopPut: objectMemory nilObject.
^self transferTo: self wakeHighestPriority].
myList := objectMemory fetchPointer: MyListIndex ofObject: process.
myContext := objectMemory fetchPointer: SuspendedContextIndex ofObject: process.
((objectMemory isPointers: myList)
and: [(objectMemory numSlotsOf: myList) > LastLinkIndex
and: [(objectMemory isContext: myContext)
and: [self isResumableContext: myContext]]]) ifFalse:
[^self primitiveFailFor: PrimErrBadReceiver].
ok := self removeProcess: process fromList: myList.
ok ifFalse:
[^self primitiveFailFor: PrimErrOperationFailed].
- objectMemory storePointerUnchecked: MyListIndex ofObject: process withValue: objectMemory nilObject.
(objectMemory fetchClassTagOfNonImm: myList) ~= classLinkedListClassTag
ifTrue:
[self backupContext: myContext toBlockingSendTo: myList.
self stackTopPut: objectMemory nilObject]
ifFalse:
[self stackTopPut: myList]!