Eliot Miranda uploaded a new version of VMMaker to project VM Maker: http://source.squeak.org/VMMaker/VMMaker.oscog-LM.3334.mcz
==================== Summary ====================
Name: VMMaker.oscog-LM.3334 Author: eem Time: 22 August 2023, 1:09:54.627877 pm UUID: 82305b00-ff38-47b9-978b-a25dc44258c8 Ancestors: VMMaker.oscog-eem.3333
First commit of Leon Matthes' work to revive the threaded FFI (committed by eem on behalf of LM cuz of Monticello permissions on source.squeak.org).
Fix simulation so that the current processor register state reflects the current Smalltalk process. Eliot's first attempt was a bit broken. This approach of manually switching register states in tryLockVMOwnerTo: preserves fast simulation because we're not changing register state on every send to the processor object as was the case with the original MultiProcessor wrapper. Check for a missing register state by setting the register state for stack, frame & pc pointers to zero. Label the processes spawned by the simulation so they show up nicely in the process browser.
releaseVM was used several places where disownVM: should have been used.
Simplify cedeToHigherPriorityThreads; the VM can't be unowned when invoked.
Nuke some unneeded halts.
=============== Diff against VMMaker.oscog-eem.3333 ===============
Item was changed: ----- Method: CoInterpreterMT>>cedeToHigherPriorityThreads (in category 'process primitive support') ----- cedeToHigherPriorityThreads "Invoked from checkForEventsMayContextSwitch: to switch threads if a thread wanting to acquire the VM has higher priority than the active process." + | activeProc processAffinity activeContext activePriority activeThread vmThread | - | activeProc ownerIndex activeContext activePriority activeThread vmThread | <var: #activeThread type: #'CogVMThread *'> <var: #vmThread type: #'CogVMThread *'> <inline: false> activeProc := self activeProcess. activePriority := self quickFetchInteger: PriorityIndex ofObject: activeProc. + processAffinity := self ownerIndexOfProcess: activeProc. + activeThread := cogThreadManager currentVMThread. + self assert: (cogThreadManager threadIndex: activeThread index isCompatibleWith: processAffinity). + - ownerIndex := self ownerIndexOfProcess: activeProc. - ownerIndex = 0 - ifTrue: [activeThread := cogThreadManager currentVMThread] - ifFalse: [activeThread := cogThreadManager vmThreadAt: ownerIndex]. activeThread priority: activePriority. vmThread := cogThreadManager highestPriorityThreadIfHigherThan: activePriority expectedMax: maxWaitingPriority. (vmThread isNil "no waiting thread of sufficiently high priority. Do not switch." or: [vmThread = activeThread]) "The activeProcess needs to run on a different thread. Leave this to threadSwitchIfNecessary:from: in checkForEventsMayContextSwitch:" ifTrue: [maxWaitingPriority > activePriority ifTrue: [maxWaitingPriority := activePriority]. ^self].
self assert: vmThread priority > activePriority. self assert: vmThread ~= cogThreadManager currentVMThread. self assertValidExecutionPointe: instructionPointer r: framePointer s: stackPointer.
maxWaitingPriority > vmThread priority ifTrue: [maxWaitingPriority := vmThread priority]. statProcessSwitch := statProcessSwitch + 1. activeContext := self ensureFrameIsMarried: framePointer SP: stackPointer. objectMemory storePointer: SuspendedContextIndex ofObject: activeProc withValue: activeContext. self ensurePushedInstructionPointer. self externalWriteBackHeadFramePointers. self putToSleep: activeProc yieldingIf: preemptionYields. "Transcript cr; print: #cedeToHighestPriorityThreadIfHigherThan:; cr. self printExternalHeadFrame. self print: 'ip: '; printHex: self instructionPointer. Transcript cr; flush." self returnToSchedulingLoopAndReleaseVMOrWakeThread: vmThread source: CSCheckEvents!
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]. 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]. - [cogThreadManager saveRegisterStateForCurrentProcess]. (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>>ownVMFromUnidentifiedThread (in category 'vm scheduling') ----- ownVMFromUnidentifiedThread "Attempt to take ownership from a thread that as yet doesn't know its index. This supports callbacks where the callback could originate from any thread. Answer 0 if the owning thread is known to the VM. Answer 1 if the owning thread is unknown to the VM and now owns the VM. Answer -1 if the owning thread is unknown to the VM and fails to own the VM. Answer -2 if the owning thread is unknown to the VM and there is no foreign callback process installed." | count threadIndex vmThread | <var: #vmThread type: #'CogVMThread *'> <inline: false> + + self cCode: [] inSmalltalk: [self halt: 'TODO: Implement processor register switching']. (threadIndex := cogThreadManager ioGetThreadLocalThreadIndex) ~= 0 ifTrue: [ "this is a callback from a known thread" (cogThreadManager vmOwnerIs: threadIndex) ifTrue: "the VM has not been disowned" [self assert: (disowningVMThread isNil or: [disowningVMThread = self currentVMThread]). disowningVMThread := nil. self currentVMThread state: CTMAssignableOrInVM. ^VMAlreadyOwnedHenceDoNotDisown]. ^self ownVM: threadIndex]. foreignCallbackPriority = 0 ifTrue: [^-2]. count := 0. "If the current thread doesn't have an index it's new to the vm and we need to allocate a new threadInfo, failing if we can't. We also need a process in the foreignCallbackProcessSlot upon which to run the thread's eventual callback." [[cogThreadManager tryLockVMOwnerTo: cogThreadManager ioCurrentOSThread asUnsignedInteger] whileFalse: [self waitingPriorityIsAtLeast: foreignCallbackPriority. cogThreadManager ioTransferTimeslice]. (objectMemory splObj: foreignCallbackProcessSlot) ~= objectMemory nilObject] whileFalse: [cogThreadManager releaseVM. (count := count + 1) > 1000 ifTrue: [^-2]. cogThreadManager ioMilliSleep: 1].
vmThread := cogThreadManager unusedThreadInfo. "N.B. Keep the VM locked anonymously so that we reserve the non-nil ForeignCallbackProcess for this thread, avoiding the race between competing foreign callbacks. The acquireVMFor: in ownVM: will set the vmOwner to the actual index. So only unlock on failure." vmThread ifNil: [cogThreadManager releaseVM. ^-1]. cogThreadManager setVMOwner: vmThread index. vmThread state: CTMWantingOwnership; priority: foreignCallbackPriority. cogThreadManager registerVMThread: vmThread. ^self ownVM: vmThread index + OwnVMForeignThreadFlag!
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]]. 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.
- self halt. 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>>returnToSchedulingLoopAndReleaseVMOrWakeThread:source: (in category 'process primitive support') ----- returnToSchedulingLoopAndReleaseVMOrWakeThread: vmThread source: source <var: #vmThread type: #'CogVMThread *'> <inline: false> self recordThreadSwitchTo: (vmThread ifNotNil: [vmThread index] ifNil: [0]) source: source. vmThread ifNotNil: [cogThreadManager wakeVMThreadFor: vmThread index] + ifNil: [cogit disownVM: DisownVMForThreading]. - ifNil: [cogThreadManager releaseVM]. "I am not frightened of flying. Any value will do. I don't mind. Why should I be frightened of flying? There's no reason for it." self _longjmp: reenterThreadSchedulingLoop _: 1 !
Item was changed: ----- Method: CoInterpreterMT>>threadSchedulingLoopImplementation: (in category 'vm scheduling') ----- threadSchedulingLoopImplementation: vmThread "Enter a loop attempting to run the VM with the highest priority process and blocking on the thread's OS semaphore when unable to run that process. We will return to this via threadSwitchIfNecessary:from: which is called in the middle of transferTo:from: once the active process has been stored in the scheduler." <var: #vmThread type: #'CogVMThread *'> <inline: false> self _setjmp: reenterThreadSchedulingLoop. [self assert: vmThread state = CTMAssignableOrInVM. (cogThreadManager tryLockVMOwnerTo: vmThread index) ifTrue: ["If relinquishing is true, then primitiveRelinquishProcessor has disowned the VM and only a returning call or callback should take ownership in that case." relinquishing + ifTrue: [cogThreadManager disownVM: DisownVMForThreading] - ifTrue: [cogThreadManager releaseVM] ifFalse: [self tryToExecuteSmalltalk: vmThread]]. (cogThreadManager vmOwnerIs: vmThread index) ifFalse: [cogThreadManager waitForWork: vmThread]. true] whileTrue!
Item was changed: ----- Method: CoInterpreterMT>>tryToExecuteSmalltalk: (in category 'vm scheduling') ----- tryToExecuteSmalltalk: vmThread "Attempt to run the current process, if it exists, on the given vmThread." <var: #vmThread type: #'CogVMThread *'> | dvmt activeProc ownerIndex | <var: #dvmt type: #'CogVMThread *'> self assert: (cogThreadManager vmOwnerIs: vmThread index). self assert: cogThreadManager ioGetThreadLocalThreadIndex = vmThread index. dvmt := disowningVMThread. disowningVMThread ifNil: [activeProc := self activeProcess] ifNotNil: [self preemptDisowningThread. activeProc := self wakeHighestPriority. activeProc ifNil: [activeProc := objectMemory nilObject] ifNotNil: [objectMemory storePointerUnchecked: MyListIndex ofObject: activeProc withValue: objectMemory nilObject]. objectMemory storePointer: ActiveProcessIndex ofObject: self schedulerPointer withValue: activeProc]. activeProc = objectMemory nilObject ifTrue: + [cogThreadManager disownVM: DisownVMForThreading. - [cogThreadManager releaseVM. ^nil]. ownerIndex := self ownerIndexOfProcess: activeProc. (ownerIndex = 0 or: [cogThreadManager vmOwnerIsCompatibleWith: ownerIndex]) ifTrue: [self assert: (objectMemory fetchPointer: MyListIndex ofObject: self activeProcess) = objectMemory nilObject. (objectMemory fetchPointer: SuspendedContextIndex ofObject: activeProc) ~= objectMemory nilObject ifTrue: [self externalSetStackPageAndPointersForSuspendedContextOfProcess: activeProc]. instructionPointer = cogit ceReturnToInterpreterPC ifTrue: [self deny: (self isMachineCodeFrame: framePointer). instructionPointer := self iframeSavedIP: framePointer]. self enterSmalltalkExecutive. "When we return here we should have already given up the VM and so we cannot touch any interpreter state." "NOTREACHED"]. cogThreadManager wakeVMThreadFor: ownerIndex!
Item was added: + ----- Method: CogThreadManager>>assertEmptyRegisterStates: (in category 'simulation') ----- + assertEmptyRegisterStates: registerState + + self assert: (registerState at: cogit processor registerStateFPIndex) == 0. + self assert: (registerState at: cogit processor registerStateSPIndex) == 0. + self assert: (registerState at: cogit processor registerStatePCIndex) == 0.!
Item was added: + ----- Method: CogThreadManager>>clearRegisterStates (in category 'simulation') ----- + clearRegisterStates + <doNotGenerate> + |setters| + setters := cogit processor registerStateSetters. + cogit processor perform: (setters at: cogit processor registerStateFPIndex) with: 0. + cogit processor perform: (setters at: cogit processor registerStateSPIndex) with: 0. + cogit processor perform: (setters at: cogit processor registerStatePCIndex) with: 0. + self assertEmptyRegisterStates: cogit processor registerState.!
Item was changed: ----- Method: CogThreadManager>>ioNewOS:Thread: (in category 'simulation') ----- ioNewOS: function Thread: vmThread "Fork a new thread to evaluate function with the supplied argument. Return 0 on success and non-zero on error. The only errors equate to running out of resources, ie. memory. See platforms/Cross/vm/sq.h for the real definition." <doNotGenerate> + [self perform: function with: vmThread] forkNamed: 'CogVMThread (', vmThread index, ')'. - [self perform: function with: vmThread] fork. ^0!
Item was changed: ----- Method: CogThreadManager>>ioTransferTimeslice (in category 'simulation') ----- ioTransferTimeslice <doNotGenerate> "See platforms/Cross/vm/sq.h for the real definition." - self saveRegisterStateForCurrentProcess. Processor yield!
Item was changed: ----- Method: CogThreadManager>>ioWaitOnOSSemaphore: (in category 'simulation') ----- ioWaitOnOSSemaphore: aSemaphorePtr <var: #anOSSemaphore type: #'sqOSSemaphore *'> <returnTypeC: #void> <doNotGenerate> "See platforms/Cross/vm/sq.h for the real definition." "Simulate the VM's heartbeat by calling checkVMOwnershipFromHeartbeat if the wait times-out." - self saveRegisterStateForCurrentProcess. - [aSemaphorePtr value waitTimeoutMSecs: 1000] whileTrue: [coInterpreter checkVMOwnershipFromHeartbeat]!
Item was added: + ----- Method: CogThreadManager>>loadOrInitializeRegisterStateFor: (in category 'simulation') ----- + loadOrInitializeRegisterStateFor: threadIndex + <doNotGenerate> + |processor| + processor := cogit processor. + processor setRegisterState: (registerStates + at: threadIndex + ifAbsentPut: + [self initializeProcessor: processor forThreadIndex: threadIndex. + processor registerState]).!
Item was changed: ----- Method: CogThreadManager>>registerVMThread: (in category 'scheduling') ----- registerVMThread: vmThread "Register the current thread in the set of threads, initializing the supplied vmThread and setting the thread's thread block index there-to." <var: #vmThread type: #'CogVMThread *'> <returnTypeC: #'CogVMThread *'> + self assert: ((vmThread state = CTMInitializing + or: [vmThread state = CTMWantingOwnership]) and: [vmThread osThread isNil]). - self assert: (vmThread state = CTMInitializing - or: [vmThread state = CTMWantingOwnership]). vmThread osThread: self ioCurrentOSThread. self ioSetThreadLocalThreadIndex: vmThread index. self assert: self ioGetThreadLocalThreadIndex = vmThread index. ^vmThread!
Item was changed: ----- Method: CogThreadManager>>saveRegisterStateForCurrentProcess (in category 'simulation') ----- saveRegisterStateForCurrentProcess "On switching osProcess we have to both - SAVE old process's register state - RESTORE new process's register state So what are the transitions? The transitions out (where processor state must be saved) are the opposites of tryLockVMOwnerTo:. Hence - waitOnOSSemaphore: - disownVM: - ioTransferTimeslice i.e. the continuations from here, disownVM:, and ioTransferTimeslice, will use tryLockVMOwnerTo: to regain control of the VM. So the register state to be restored at that point (if tryLockVMOwnerTo: succeeds) is the register state saved in one of the three places. But the processor is initialized in tryLockVMOwnerTo:, so only save the state if state is already present." <doNotGenerate> cogit withProcessorHaltedDo: [| currentVMThread state | currentVMThread := self vmThreadForCurrentProcess. + state := cogit processor registerState. + self assertValidStackPointersInState: state forIndex: currentVMThread index. + registerStates at: currentVMThread index put: state]! - (registerStates includesKey: currentVMThread index) ifTrue: - [state := cogit processor registerState. - self assertValidStackPointersInState: state forIndex: currentVMThread index. - registerStates at: currentVMThread index put: state]]!
Item was changed: ----- Method: CogThreadManager>>setVMOwner: (in category 'public api') ----- setVMOwner: indexOrZero "An ugly accessor used in only three cases: 1. by ownVMFromUnidentifiedThread when the VM is first locked to the thread id of the unidentified thread, and then, once identified, to the thread's index. 2. by wakeVMThreadFor: used by the two-level scheduler to switch threads when a Smalltalk process switch occurs to a process affined to another thread. 3. to release the VM (set the owner to zero)" <inline: #always> + self assert: (vmOwner ~= 0 and: [vmOwner ~= indexOrZero]). self cCode: '' inSmalltalk: [coInterpreter transcript ensureCr; f: 'setVMOwner: %d -> %d (%s)\n' printf: { vmOwner. indexOrZero. thisContext home sender selector }]. + + "With the assignment followed by a LowLevelMFence this is equivalent to a + Fetch-Assign with an C++ Memory ordering of Squentially-Consistent. + TODO: We could make this a `release` ordering, which may perform better on ARM." vmOwner := indexOrZero. self sqLowLevelMFence!
Item was changed: ----- Method: CogThreadManager>>threadIndex:isCompatibleWith: (in category 'public api-testing') ----- threadIndex: aThreadIndex isCompatibleWith: processThreadId "Test if processThreadId is ok to run on a thread with the given index." <inline: true> self assert: aThreadIndex > 0. + ^processThreadId = 0 + or: [processThreadId >= 0 + ifTrue: [aThreadIndex = processThreadId] + ifFalse: [aThreadIndex ~= processThreadId negated]]! - ^processThreadId >= 0 - ifTrue: [aThreadIndex = processThreadId] - ifFalse: [aThreadIndex ~= processThreadId negated]!
Item was changed: ----- Method: CogThreadManager>>tryLockVMOwnerTo: (in category 'simulation') ----- tryLockVMOwnerTo: threadIndex "In the real VM this is a direct call of Cogit>>#tryLockVMOwnerTo:/ceTryLockVMOwner. In the simulation this is where register state is restored, simulating a thread switch. State is stored in saveRegisterStateForCurrentProcess (sent by disownVM:, ioWaitOnOSSemaphore: and ioTransferTimeslice). The code here and in saveRegisterStateForCurrentProcess allow us to avoid the expensive and complex MultiProcessor hack.
The idea here is to save the register state around the invocation of tryLockVMOwnerTo:, and set the register state to that for the owner, changing the state if ownership has changed, restoring it if ownership has not changed." <doNotGenerate> self deny: threadIndex = 0. ^cogit withProcessorHaltedDo: + [| previousOwner currentOwner processor result priorState | - [| currentOwner prior processor result | processor := cogit processor. + "After switching, the 'current' owner will be the 'previous' owner. + Though the value will be the same, let's still introduce a second variable that we + can use after the switch to make it more clear what's going on." + previousOwner := currentOwner := vmOwner. + + priorState := processor registerState. + "If we currently have a VM owner, the register state should be + valid for that owner, otherwise it should be empty." + vmOwner ~= 0 + ifTrue: [self assertValidStackPointersInState: priorState forIndex: vmOwner] + ifFalse: [self assertEmptyRegisterStates: priorState]. + + vmOwner ~= threadIndex ifTrue: + ["There can be two cases here: + 1. There's currently a VM owner, and the processor has a non-empty state. + 2. There is currently no VM owner and the processor has an empty state. + In both cases, we need to restore our own register state into the processor, + as cogit>>#tryLockVMOwnerTo: call will use the processor, so the processor + cannot be empty and we also don't want the processor modify the current + owners state." + self loadOrInitializeRegisterStateFor: threadIndex]. + - currentOwner := vmOwner. - vmOwner ~= 0 ifTrue: - [prior := processor registerState. - self assertValidStackPointersInState: prior forIndex: vmOwner]. - processor setRegisterState: (registerStates - at: threadIndex - ifAbsentPut: - [self initializeProcessor: processor forThreadIndex: threadIndex. - processor registerState]). result := cogit tryLockVMOwnerTo: threadIndex. self assert: result = (threadIndex = vmOwner). + result + ifTrue: ["If we did actually change owners, assert that previously the processor was emtpy." + previousOwner ~= vmOwner ifTrue: [self assertEmptyRegisterStates: priorState]] + ifFalse: [processor setRegisterState: priorState.]. + - ifTrue: [registerStates at: threadIndex put: processor registerState] - ifFalse: [prior ifNotNil: [processor setRegisterState: prior]]. coInterpreter transcript ensureCr; f: (result ifTrue: ['tryLockVMOwner %d -> %d (%s) ok\n'] ifFalse: ['tryLockVMOwner %d -> %d (%s) FAILED\n']) + printf: { previousOwner. threadIndex. thisContext home sender selector }. - printf: { vmOwner. threadIndex. thisContext home sender selector }. self assertValidProcessorStackPointersForIndex: vmOwner. result]!
Item was changed: ----- Method: CogThreadManager>>wakeVMThreadFor: (in category 'public api') ----- wakeVMThreadFor: index "Transfer the VM to the thread with index. Called from a thread that finds the highest priority runnable process is bound to the thread with index index." <returnTypeC: #void> | vmThread | self assert: (self vmIsOwned and: [(self vmOwnerIs: index) not]). self assert: (index between: 1 and: numThreads). - self setVMOwner: index. vmThread := threads at: index. + + "Instead of going through a #disownVM: call, directly set the new VM owner. + This has the advantage of avoiding a race for the different threads to become the new + VM owner. + In Simulation, this means we need to simulate a thread-switch." + self cCode: [] inSmalltalk: + [self saveRegisterStateForCurrentProcess. + self loadOrInitializeRegisterStateFor: index]. + self setVMOwner: index. + vmThread state ifNil: [self startThreadForThreadInfo: vmThread] ifNotNil: [self assert: (vmThread state = CTMWantingOwnership or: [vmThread state = CTMAssignableOrInVM]). self ioSignalOSSemaphore: (self addressOf: vmThread osSemaphore)]. self ioTransferTimeslice!
vm-dev@lists.squeakfoundation.org