Eliot Miranda uploaded a new version of VMMaker to project VM Maker: http://source.squeak.org/VMMaker/VMMaker.oscog-eem.3335.mcz
==================== Summary ====================
Name: VMMaker.oscog-eem.3335 Author: eem Time: 22 August 2023, 1:29:56.927443 pm UUID: 1a04daa4-b43d-4e1f-9dc1-e9dd95701ce3 Ancestors: VMMaker.oscog-LM.3334
Rename CoInterpreterMT's processHasThreadId inst var to processHasThreadAffinity in the wake of Kernel-eem.1523.
=============== Diff against VMMaker.oscog-LM.3334 ===============
Item was changed: ----- Method: CoInterpreter>>setImageHeaderFlags: (in category 'internal interpreter access') ----- setImageHeaderFlags: flags "Set an array of flags indicating various properties of the saved image, responded to on image load. These are the same as the image header flags shifted right two bits, omitting the fullScreenFlag and float byte order flag. + Bit 0: if set, implies the image's Process class has threadAffinity as its 3rd inst var (zero relative) (meaningful to the MT VM only) - Bit 0: if set, implies the image's Process class has threadId as its 3rd inst var (zero relative) (meaningful to the MT VM only) Bit 1: if set, methods that are interpreted will have the flag bit set in their header Bit 2: if set, implies preempting a process does not put it to the back of its run queue Bit 3: if set, implies a threaded VM will not disown the VM if owned by the GUI thread (meaningful to the MT VM only) Bit 4: if set, implies the new finalization scheme where WeakArrays are queued Bit 5: if set, implies wheel events will be delivered as such and not mapped to arrow key events Bit 6: if set, implies arithmetic primitives will fail if given arguments of different types (float vs int) Bit 7: if set, implies file primitives (FilePlugin, FileAttributesPlugin) will answer file times in UTC not local times Bit 8: if set, implies the VM will not upscale the display on high DPI monitors; older VMs did this by default." flags asUnsignedInteger > 511 ifTrue: [^self primitiveFailFor: PrimErrUnsupported]. + "processHasThreadAffinity := flags anyMask: 1. specific to CoInterpreterMT" - "processHasThreadId := flags anyMask: 1. specific to CoInterpreterMT" imageHeaderFlags := (flags anyMask: 1) ifTrue: [imageHeaderFlags bitOr: 4] ifFalse: [imageHeaderFlags bitClear: 4]. flagInterpretedMethods := flags anyMask: 2. preemptionYields := flags noMask: 4. "noThreadingOfGUIThread := flags anyMask: 8.. specific to CoInterpreterMT" imageHeaderFlags := (flags anyMask: 8) ifTrue: [imageHeaderFlags bitOr: 32] ifFalse: [imageHeaderFlags bitClear: 32]. newFinalization := flags anyMask: 16. sendWheelEvents := flags anyMask: 32. primitiveDoMixedArithmetic := flags noMask: 64. imageHeaderFlags := (flags anyMask: 128) ifTrue: [imageHeaderFlags bitOr: 512] ifFalse: [imageHeaderFlags bitClear: 512]. upscaleDisplayIfHighDPI := flags noMask: 256!
Item was changed: CoInterpreterPrimitives subclass: #CoInterpreterMT + instanceVariableNames: 'cogThreadManager checkThreadActivation maxWaitingPriority foreignCallbackPriority deferThreadSwitch disowningVMThread disownCount foreignCallbackProcessSlot activeProcessAffined relinquishing reenterThreadSchedulingLoop willNotThreadWarnCount processHasThreadAffinity' - instanceVariableNames: 'cogThreadManager checkThreadActivation maxWaitingPriority foreignCallbackPriority deferThreadSwitch disowningVMThread disownCount foreignCallbackProcessSlot activeProcessAffined relinquishing processHasThreadId reenterThreadSchedulingLoop willNotThreadWarnCount' classVariableNames: 'DisownFlagsShift DisownVMForProcessorRelinquish OwnVMForeignThreadFlag PerThreadRumpCStackSize PrimNumberRelinquishProcessor ProcessUnaffinedOnDisown ReturnToThreadSchedulingLoop VMAlreadyOwnedHenceDoNotDisown' poolDictionaries: 'VMThreadingConstants' category: 'VMMaker-Multithreading'!
Item was changed: ----- 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 - id 0 nothing to do - id 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 - id = affined index nothing to do - id = 0 nothing to do - id ~= 0 && id ~= affined index fail process is bound (permanently bound to a thread) + affinity = bound index nothing to do + affinity ~= bound index set bound index" - id = bound index nothing to do - id ~= bound index set bound index" | threadIdField ownerIndex affinedId | + processHasThreadAffinity ifFalse: - processHasThreadId ifFalse: [^PrimErrUnsupported].
+ threadIdField := self threadAffinityFieldOf: aProcess. - threadIdField := self threadIdFieldOf: 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 changed: ----- Method: CoInterpreterMT>>checkVMOwnershipFromHeartbeat (in category 'process primitive support') ----- checkVMOwnershipFromHeartbeat "Check whether the VM is unowned and needs to set a thread running to try and own it. + Do not attempt this if the image doesn't have a threadAffinity inst var in Process; the VM - Do not attempt this if the image doesn't have a threadId inst var in Process; the VM can't thread these images." <inline: false> self sqLowLevelMFence. + (processHasThreadAffinity - (processHasThreadId and: [cogThreadManager vmIsUnowned]) ifTrue: [cogThreadManager ensureRunningVMThread: relinquishing]!
Item was changed: ----- Method: CoInterpreterMT>>disownVM: (in category 'vm scheduling') ----- disownVM: flags "Release the VM to other threads and answer the current thread's index. Currently valid flags: DisownVMForFFICall - informs the VM that it is entering an FFI call DisownVMForThreading - informs the VM that it is entering code during which threading should be permitted OwnVMForeignThreadFlag - indicates lowest-level entry from a foreign thread - not to be used explicitly by clients - only set by ownVMFromUnidentifiedThread VMAlreadyOwnedHenceDoNotDisown - indicates an ownVM from a callback was made when the vm was still owned. - not to be used explicitly by clients - only set by ownVMFromUnidentifiedThread
This is the entry-point for plugins and primitives that wish to release the VM while performing some operation that may potentially block, and for callbacks returning back to some blocking operation. If this thread does not reclaim the VM before- hand then when the next heartbeat occurs the thread manager will schedule a thread to acquire the VM which may start running the VM in place of this thread.
N.B. Most of the state needed to resume after preemption is set in preemptDisowningThread." <public> <inline: false> | vmThread result | self assert: (flags >= 0 and: [flags < (1 bitShift: DisownFlagsShift)]). self assert: self successful. cogit recordEventTrace ifTrue: [self recordTrace: TraceDisownVM thing: (objectMemory integerObjectOf: flags) source: 0]. + processHasThreadAffinity ifFalse: - processHasThreadId ifFalse: [willNotThreadWarnCount < 10 ifTrue: [self print: 'warning: VM parameter 48 indicates Process doesn''t have threadId; VM will not thread'; cr. willNotThreadWarnCount := willNotThreadWarnCount + 1]]. vmThread := cogThreadManager currentVMThread. (flags anyMask: VMAlreadyOwnedHenceDoNotDisown) ifTrue: [disowningVMThread := vmThread. vmThread state: CTMUnavailable. ^0]. self assertCStackPointersBelongToCurrentThread. self assertValidNewMethodPropertyFlags. self cCode: '' inSmalltalk: [cogThreadManager saveRegisterStateForCurrentProcess. cogThreadManager clearRegisterStates]. (flags anyMask: DisownVMForProcessorRelinquish) ifTrue: [| proc | (proc := objectMemory splObj: foreignCallbackProcessSlot) ~= objectMemory nilObject ifTrue: [foreignCallbackPriority := self quickFetchInteger: PriorityIndex ofObject: proc]. relinquishing := true. self sqLowLevelMFence]. disownCount := disownCount + 1. disowningVMThread := vmThread. "self cr; cr; print: 'disownVM Csp: '; printHex: vmThread cStackPointer; cr. (0 to: 16 by: 4) do: [:offset| self print: ' *(esp+'; printNum: offset; print: ': '; printHex: (stackPages longAt: cogit processor sp + offset); cr]. cogit processor printIntegerRegistersOn: Transcript."
"OwnVMForeignThreadFlag indicates lowest-level of entry by a foreign thread. If that's where we are then release the vmThread. Otherwise indicate the vmThread is off doing something outside of the VM." (flags anyMask: OwnVMForeignThreadFlag) ifTrue: ["I don't think this is quite right. Josh's use case is creating some foreign thread and then registering it with the VM. That's not the same as binding a process to a foreign thread given that the foreign callback process is about to terminate anyway (it is returning from a callback here). So do we need an additional concept, that of a vmThread being either of the set known to the VM or floating?" self flag: 'issue with registering foreign threads with the VM'. (self isBoundProcess: self activeProcess) ifFalse: [cogThreadManager unregisterVMThread: vmThread]] ifFalse: [vmThread state: CTMUnavailable]. result := ((vmThread index bitShift: DisownFlagsShift) bitOr: (activeProcessAffined ifTrue: [0] ifFalse: [ProcessUnaffinedOnDisown])) bitOr: flags. cogThreadManager releaseVM. ^result!
Item was changed: ----- Method: CoInterpreterMT>>getImageHeaderFlags (in category 'image save/restore') ----- getImageHeaderFlags "Answer the flags that are contained in the 7th long of the image header." ^fullScreenFlag "0 or 1" + (VMBIGENDIAN ifTrue: [0] ifFalse: [2]) "this is the imageFloatsLittleEndian flag" + + (processHasThreadAffinity ifTrue: [4] ifFalse: [0]) - + (processHasThreadId ifTrue: [4] ifFalse: [0]) + (flagInterpretedMethods ifTrue: [8] ifFalse: [0]) + (preemptionYields ifTrue: [0] ifFalse: [16r10]) "was: noThreadingOfGUIThread ifTrue: [16r20] ifFalse: [0]); a broken idea" + (newFinalization ifTrue: [16r40] ifFalse: [0]) + (sendWheelEvents ifTrue: [16r80] ifFalse: [0]) + (primitiveDoMixedArithmetic ifTrue: [0] ifFalse: [16r100]) "N.B. flag mask 16r200 is fileTimesInUTC, responded to by the FilePlugin & FileAttributesPlugin" + (upscaleDisplayIfHighDPI ifTrue: [0] ifFalse: [16r400]) + (imageHeaderFlags bitClear: 16r7FF) "these are any flags we do not recognize"!
Item was changed: ----- Method: CoInterpreterMT>>isAffinedProcess: (in category 'process primitive support') ----- isAffinedProcess: aProcess + ^self isAffinedThreadId: (self threadAffinityFieldValueOf: aProcess)! - ^self isAffinedThreadId: (self threadIdFieldValueOf: aProcess)!
Item was changed: ----- Method: CoInterpreterMT>>ownerIndexOfProcess: (in category 'process primitive support') ----- ownerIndexOfProcess: aProcess + ^self ownerIndexOfThreadId: (self threadAffinityFieldOf: aProcess)! - ^self ownerIndexOfThreadId: (self threadIdFieldOf: aProcess)!
Item was changed: ----- 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 | <export: true> self cCode: [] inSmalltalk: [cogThreadManager isNil ifTrue: [^self primitiveFail]]. + processHasThreadAffinity ifFalse: - processHasThreadId 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.
id := self ownerIndexOfProcess: aProcess. (aProcess = self activeProcess and: [(activeProcessAffined := id ~= 0) and: [(cogThreadManager vmOwnerIsCompatibleWith: id) not]]) ifTrue: [(self quickFetchInteger: PriorityIndex ofObject: aProcess) < maxWaitingPriority ifTrue: [maxWaitingPriority = self quickFetchInteger: PriorityIndex ofObject: aProcess]. self threadSwitchIfNecessary: aProcess from: CSThreadBind]!
Item was changed: ----- Method: CoInterpreterMT>>primitiveProcessBoundThreadId (in category 'process primitives') ----- primitiveProcessBoundThreadId + "Answer the receiver's current threadAffinity or nil, where the receiver is a Process. + If the threadAffinity is positive then the receiver is bound to the thread with that id. + If the threadAffinity is negative then the receiver is excluded from running on the thread with that id." - "Answer the receiver's current thread Id or nil, where the receiver is a Process. - If the threadId is positive then the receiver is bound to the thread with that id. - If the threadId is negative then the receiver is excluded from running on the thread with that id." | aProcess id | <export: true> self cCode: [] inSmalltalk: [cogThreadManager isNil ifTrue: [^self primitiveFail]]. + processHasThreadAffinity ifFalse: - processHasThreadId ifFalse: [^self primitiveFailFor: PrimErrUnsupported]. aProcess := self stackTop. id := self ownerIndexOfProcess: aProcess. self methodReturnValue: (id = 0 ifTrue: [objectMemory nilObject] ifFalse: [objectMemory integerObjectOf: id])!
Item was changed: ----- Method: CoInterpreterMT>>setImageHeaderFlags: (in category 'internal interpreter access') ----- setImageHeaderFlags: flags "Set an array of flags indicating various properties of the saved image, responded to on image load. These are the same as the image header flags shifted right two bits, omitting the fullScreenFlag and float byte order flag. + Bit 0: if set, implies the image's Process class has threadAffinity as its 3rd inst var (zero relative) - Bit 0: if set, implies the image's Process class has threadId as its 3rd inst var (zero relative) Bit 1: if set, methods that are interpreted will have the flag bit set in their header Bit 2: if set, implies preempting a process does not put it to the back of its run queue Bit 3: unassigned; used to mean if set, implies a threaded VM will not disown the VM if owned by the GUI thread; a broken idea Bit 4: if set, implies the new finalization scheme where WeakArrays are queued Bit 5: if set, implies wheel events will be delivered as such and not mapped to arrow key events Bit 6: if set, implies arithmetic primitives will fail if given arguments of different types (float vs int) Bit 7: if set, implies file primitives (FilePlugin, FileAttributesPlugin) will answer file times in UTC not local times Bit 8: if set, implies the VM will not upscale the display on high DPI monitors; older VMs did this by default." flags asUnsignedInteger > 511 ifTrue: [^self primitiveFailFor: PrimErrUnsupported]. (flags anyMask: 8) ifTrue: [^self primitiveFailFor: PrimErrInappropriate]. + processHasThreadAffinity := flags anyMask: 1. - processHasThreadId := flags anyMask: 1. flagInterpretedMethods := flags anyMask: 2. preemptionYields := flags noMask: 4. "was: noThreadingOfGUIThread := flags anyMask: 8. a broken idea" newFinalization := flags anyMask: 16. sendWheelEvents := flags anyMask: 32. primitiveDoMixedArithmetic := flags noMask: 64. imageHeaderFlags := (flags anyMask: 128) ifTrue: [imageHeaderFlags bitOr: 512] ifFalse: [imageHeaderFlags bitClear: 512]. upscaleDisplayIfHighDPI := flags noMask: 256!
Item was changed: ----- Method: CoInterpreterMT>>setImageHeaderFlagsFrom: (in category 'image save/restore') ----- setImageHeaderFlagsFrom: headerFlags "Set the flags that are contained in the 7th long of the image header." imageHeaderFlags := headerFlags. "so as to preserve unrecognised flags." fullScreenFlag := headerFlags bitAnd: 1. imageFloatsBigEndian := (headerFlags noMask: 2) ifTrue: [1] ifFalse: [0]. + processHasThreadAffinity := headerFlags anyMask: 4. - processHasThreadId := headerFlags anyMask: 4. flagInterpretedMethods := headerFlags anyMask: 8. preemptionYields := headerFlags noMask: 16. "was: noThreadingOfGUIThread := headerFlags anyMask: 32. a broken idea" newFinalization := headerFlags anyMask: 64. sendWheelEvents := headerFlags anyMask: 128. primitiveDoMixedArithmetic < 0 ifTrue: "i.e. has it not been set on the command line?" [primitiveDoMixedArithmetic := headerFlags noMask: 256]. "N.B. flag mask 512 is responded to by the FilePlugin & FileAttributesPlugin" upscaleDisplayIfHighDPI < 0 ifTrue: "i.e. has it not been set on the command line?" [upscaleDisplayIfHighDPI := headerFlags noMask: 1024].
+ processHasThreadAffinity ifFalse: + [self print: 'warning, processHasThreadAffinity flag is unset; cannot function as a threaded VM if so.'; cr]! - processHasThreadId ifFalse: - [self print: 'warning, processHasThreadId flag is unset; cannot function as a threaded VM if so.'; cr]!
Item was added: + ----- Method: CoInterpreterMT>>threadAffinityFieldOf: (in category 'process primitive support') ----- + threadAffinityFieldOf: aProcess + ^processHasThreadAffinity + ifTrue: [objectMemory fetchPointer: ThreadIdIndex ofObject: aProcess] + ifFalse: [objectMemory nilObject]!
Item was added: + ----- Method: CoInterpreterMT>>threadAffinityFieldValueOf: (in category 'process primitive support') ----- + threadAffinityFieldValueOf: aProcess + ^processHasThreadAffinity + ifTrue: + [| field | + field := objectMemory fetchPointer: ThreadIdIndex ofObject: aProcess. + field = objectMemory nilObject + ifTrue: [0] + ifFalse: [objectMemory integerValueOf: field]] + ifFalse: [0]!
Item was removed: - ----- Method: CoInterpreterMT>>threadIdFieldOf: (in category 'process primitive support') ----- - threadIdFieldOf: aProcess - ^processHasThreadId - ifTrue: [objectMemory fetchPointer: ThreadIdIndex ofObject: aProcess] - ifFalse: [objectMemory nilObject]!
Item was removed: - ----- Method: CoInterpreterMT>>threadIdFieldValueOf: (in category 'process primitive support') ----- - threadIdFieldValueOf: aProcess - ^processHasThreadId - ifTrue: - [| field | - field := objectMemory fetchPointer: ThreadIdIndex ofObject: aProcess. - field = objectMemory nilObject - ifTrue: [0] - ifFalse: [objectMemory integerValueOf: field]] - ifFalse: [0]!
Item was added: + ----- Method: CogVMSimulator>>threadAffinityFieldOf: (in category 'multi-threading simulation switch') ----- + threadAffinityFieldOf: aProcess + "This method includes or excludes CoInterpreterMT methods as required. + Auto-generated by CogVMSimulator>>ensureMultiThreadingOverridesAreUpToDate" + + ^self perform: #threadAffinityFieldOf: + withArguments: {aProcess} + inSuperclass: (cogThreadManager ifNil: [CoInterpreterPrimitives] ifNotNil: [CoInterpreterMT])!
Item was removed: - ----- Method: CogVMSimulator>>threadIdFieldOf: (in category 'multi-threading simulation switch') ----- - threadIdFieldOf: aProcess - "This method includes or excludes CoInterpreterMT methods as required. - Auto-generated by CogVMSimulator>>ensureMultiThreadingOverridesAreUpToDate" - - ^self perform: #threadIdFieldOf: - withArguments: {aProcess} - inSuperclass: (cogThreadManager ifNil: [CoInterpreterPrimitives] ifNotNil: [CoInterpreterMT])!
Item was changed: ----- Method: StackInterpreter>>setImageHeaderFlags: (in category 'internal interpreter access') ----- setImageHeaderFlags: flags "Set an array of flags indicating various properties of the saved image, responded to on image load. These are the same as the image header flags shifted right two bits, omitting the fullScreenFlag and float byte order flag. + Bit 0: if set, implies the image's Process class has threadAffinity as its 3rd inst var (zero relative) (meaningful to the MT VM only) - Bit 0: if set, implies the image's Process class has threadId as its 3rd inst var (zero relative) (meaningful to the MT VM only) Bit 1: if set, methods that are interpreted will have the flag bit set in their header (meaningful to the Cog VM only) Bit 2: if set, implies preempting a process does not put it to the back of its run queue Bit 3: if set, implies a threaded VM will not disown the VM if owned by the GUI thread (meaningful to the MT VM only) Bit 4: if set, implies the new finalization scheme where WeakArrays are queued Bit 5: if set, implies wheel events will be delivered as such and not mapped to arrow key events Bit 6: if set, implies arithmetic primitives will fail if given arguments of different types (float vs int) Bit 7: if set, implies file primitives (FilePlugin, FileAttributesPlugin) will answer file times in UTC not local times Bit 8: if set, implies the VM will not upscale the display on high DPI monitors; older VMs did this by default." flags asUnsignedInteger > 511 ifTrue: [^self primitiveFailFor: PrimErrUnsupported]. + "threadAffinity := flags anyMask: 1. specific to CoInterpreterMT" - "processHasThreadId := flags anyMask: 1. specific to CoInterpreterMT" imageHeaderFlags := (flags anyMask: 1) ifTrue: [imageHeaderFlags bitOr: 4] ifFalse: [imageHeaderFlags bitClear: 4]. "flagInterpretedMethods := flags anyMask: 2. specific to CoInterpreter" imageHeaderFlags := (flags anyMask: 2) ifTrue: [imageHeaderFlags bitOr: 8] ifFalse: [imageHeaderFlags bitClear: 8]. preemptionYields := flags noMask: 4. "noThreadingOfGUIThread := flags anyMask: 8.. specific to CoInterpreterMT" imageHeaderFlags := (flags anyMask: 8) ifTrue: [imageHeaderFlags bitOr: 32] ifFalse: [imageHeaderFlags bitClear: 32]. newFinalization := flags anyMask: 16. sendWheelEvents := flags anyMask: 32. primitiveDoMixedArithmetic := flags noMask: 64. imageHeaderFlags := (flags anyMask: 128) ifTrue: [imageHeaderFlags bitOr: 512] ifFalse: [imageHeaderFlags bitClear: 512]. upscaleDisplayIfHighDPI := flags noMask: 256!
vm-dev@lists.squeakfoundation.org