[Vm-dev] VM Maker: VMMaker.oscog-eem.3098.mcz

commits at source.squeak.org commits at source.squeak.org
Fri Nov 5 23:03:43 UTC 2021


Eliot Miranda uploaded a new version of VMMaker to project VM Maker:
http://source.squeak.org/VMMaker/VMMaker.oscog-eem.3098.mcz

==================== Summary ====================

Name: VMMaker.oscog-eem.3098
Author: eem
Time: 5 November 2021, 4:03:28.570166 pm
UUID: c943325a-2d0a-48f3-ba48-8f99d804dd7f
Ancestors: VMMaker.oscog-eem.3097

CoInterpreterMT: implement "negative binding", binding a process to any thread /other than/ a specific thread.  This is a much more elegant solution to the "don't tie up the event receiving thread" issue than using a separate, non-Smalltalk thread to receive events (and in fact that solution can't work because (some time in the near future) events must be deliverable as callbacks).

=============== Diff against VMMaker.oscog-eem.3097 ===============

Item was changed:
  ----- Method: CoInterpreterMT class>>initializeMiscConstants (in category 'initialization') -----
  initializeMiscConstants
  
  	super initializeMiscConstants.
  
- 	"N.B. some of these DisownFlags are replicated in platforms/Cross/vm/sqVirtualMachine.h.
- 	 Hence they should always be initialized."
- 	DisownVMForProcessorRelinquish := 64.
- 
  	(InitializationOptions at: #COGMTVM ifAbsent: [false]) == false ifTrue:
  		[^self].
  
  	COGMTVM := true.
  
  	ReturnToThreadSchedulingLoop := 2 "setjmp/longjmp code."!

Item was changed:
  ----- Method: CoInterpreterMT class>>initializeSchedulerIndices (in category 'initialization') -----
  initializeSchedulerIndices
  	super initializeSchedulerIndices.
  	"Class Process"
  	"The thread id of a process is either nil or a SmallInteger that defines how a process binds to threads.
  	 If nil, the process may run on any thread.
  	 The least significant bit of threadId is a flag. The most significant bits are a threadId.
  	 If the threadId is nil the process can run on any thread.
  	 If the flag (least significant bit) is set then
  		If the threadId is positive the process can only be run on the thread with that thread Id.
  		If the threadId is negative the process must not be run on the thread with that thread Id.
  	 If the flag (least significant bit) is not set then
  		the thread id will not be negative and if non-zero is the id of the thread the process is currenty running on
  	 The flag is probably a mistake..."
  
  	"In part, what's really going on here is an attempt to deal with threading on Mac.  Events can only be delivered
  	 on the GUi thread. To avoid blocking and/or crashing the VM by making a blocking (e.g. FFI) call on the GUI
  	 thread we want a nice way of preventing a process from running on the GUI thread.  We can then use a process
  	 whose threadId precludes running on the GUI thread to make blocking calls.  The alternative, of arranging that
  	 events are delivered to a thread the VM does not run on, is problematic; it cannot support event callbacks."
  
  	"So if we simplify, and threadId is only instructive to the VM, (i.e. can say ''run on a given thread'', or ''don't run
  	 on a given thread'', and ''don't care; run on any thread'') and does not necessarily hold the threadId of the thread
  	 the process is currenty bound to, how do we locate the threadId for a process temporarily affined to a thread for
  	 the duration of an FFI call?  Note that a process *must* be bound to a thread for the duration of a threaded call
  	 (or foreign callback) so that return occurs on the correct thread.  We can use the least significant bit to mean
  	 ''temporarily affined'', but we need to support ''don't run on this thread''.  We could use bit fields (yuck); we
  	 could allocate a two field object and assign it to threadId when setting a process to not run on a given thread.
  
  	 This isn't so bad; use negative values to mean ''don't run on this thread'', and positive values to mean ''run on this thread''.
  	 Split the smallest SmallInteger (32-bit, 1 bit sign, 2-bit tags, leaving 29//2) into two 14 bit fields. The least significant
  	 14 bits are the thread id the receiver is temporarily affined to.  The most significant 14 bits are the thread id of the
  	 thread the proess is either bound to or excluded from.  If zero, the process is agnostic.  See CogThreadManager>>#maxNumThreads"
  	ThreadIdIndex := 4.
  	ThreadIdShift := 14. "could be 30 in 64-bits"
  
  	"disown result/own argument flags"
  	OwnVMForeignThreadFlag := 1.
  	VMAlreadyOwnedHenceDoNotDisown := 2.
  	ProcessUnaffinedOnDisown := 4.
  	"& defined in StackInterpreter are..."
  	DisownVMForFFICall := 16.
  	DisownVMForThreading := 32.
+ 	"N.B. some of these DisownFlags are replicated in platforms/Cross/vm/sqVirtualMachine.h.
+ 	 Hence they should always be initialized."
+ 	DisownVMForProcessorRelinquish := 64.
+ 
+ 	DisownFlagsShift := DisownVMForProcessorRelinquish highBit!
- 	DisownFlagsShift := DisownVMForThreading highBit!

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 state = CTMUnavailable
  				or: [disowningVMThread state = 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.
  	self assert: (objectMemory fetchPointer: MyListIndex ofObject: activeProc) = objectMemory nilObject.
  	objectMemory
  		storePointer: MyListIndex
  		ofObject: activeProc
  		withValue: (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."
  	preemptedThread := cogThreadManager pushAWOLProcess: activeProc on: disowningVMThread.
  	disowningVMThread := nil.
  	preemptedThread priority: (self quickFetchInteger: PriorityIndex ofObject: activeProc).
+ 	(self ownerIndexOfProcess: activeProc) = 0 ifTrue:
+ 		[self setOwnerIndexOfProcess: activeProc to: preemptedThread index bind: false].
- 	(self ownerIndexOfProcess: activeProc) = 0
- 		ifTrue: [self setOwnerIndexOfProcess: activeProc to: preemptedThread index bind: false]
- 		ifFalse: [self assert: (self ownerIndexOfProcess: activeProc) = preemptedThread index].
  	preemptedThread
  		newMethodOrNull: newMethod;
  		argumentCount: argumentCount;
  		primitiveFunctionPointer: primitiveFunctionPointer;
  		inMachineCode: instructionPointer <= objectMemory startOfMemory!

Item was changed:
  ----- Method: CoInterpreterMT>>threadSchedulingLoop: (in category 'vm scheduling') -----
  threadSchedulingLoop: 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.
  	 This version is for simulation only, simulating the longjmp back to the real
  	 threadSchedulingLoopImplementation: through exception handling."
  
  	<cmacro: '(vmThread) threadSchedulingLoopImplementation(vmThread)'>
  	cogit initializeProcessorStack: (self cStackRangeForThreadIndex: vmThread index) last.
  	[[self threadSchedulingLoopImplementation: vmThread]
  		on: ReenterThreadSchedulingLoop
+ 		do: [:ex|
+ 			self assert: ex returnValue = 1.
+ 			reenterThreadSchedulingLoop reset.
+ 			ex return: true]] whileTrue!
- 		do: [:ex| self assert: ex returnValue = 1. ex return: true]] whileTrue!

Item was changed:
  ----- Method: CoInterpreterMT>>threadSwitchIfNecessary:from: (in category 'process primitive support') -----
  threadSwitchIfNecessary: newProc from: sourceCode
  	"Invoked from transferTo:from: to switch threads if the new process is bound or affined to some other thread."
+ 	| newProcOwnerIndex vmThread activeContext |
- 	| newProcThreadId vmThread activeContext |
  	self assert: (cogThreadManager vmOwnerIs: cogThreadManager ioGetThreadLocalThreadIndex).
  	deferThreadSwitch ifTrue: [^self].
  
  	"If the current process is unaffined or it is affined to the current thread we're
  	 ok to run, but we should yield asap if a higher-priority t6hread wants the VM."
+ 	newProcOwnerIndex := self ownerIndexOfProcess: newProc.
+ 	((activeProcessAffined := newProcOwnerIndex ~= 0)
+ 	 and: [(cogThreadManager vmOwnerIsCompatibleWith: newProcOwnerIndex) not]) ifFalse:
- 	newProcThreadId := self ownerIndexOfProcess: newProc.
- 	((activeProcessAffined := newProcThreadId ~= 0)
- 	 and: [(cogThreadManager vmOwnerIs: newProcThreadId) not]) ifFalse:
  		[(self quickFetchInteger: PriorityIndex ofObject: newProc) < maxWaitingPriority ifTrue:
  			[checkThreadActivation := true.
  			 self forceInterruptCheck].
  		^self].
  
  	"The current process is affined to a thread, but not to the current owner.  So switch to that owner."
  	self cCode: '' inSmalltalk:
+ 		[transcript
+ 			ensureCr;
+ 			f: 'threadSwitchIfNecessary: %08x from: %d owner %d -> %d\n'
+ 			printf: { newProc. sourceCode. cogThreadManager getVMOwner. newProcOwnerIndex }].
+ 
- 		[self transcript ensureCr; nextPutAll: #threadSwitchIfNecessary:from:; space; print: newProc;
- 						space; print: cogThreadManager getVMOwner; nextPutAll: '->'; print: newProcThreadId; cr; flush].
  	 "If the activeProcess doesn't have a context yet, it needs one from which the new thread can resume execution."
  	 (objectMemory fetchPointer: SuspendedContextIndex ofObject: newProc) = objectMemory nilObject ifTrue:
  		[self assert: newProc = self activeProcess.
  		 self push: instructionPointer.
  		 self externalWriteBackHeadFramePointers.
  		 activeContext := self ensureFrameIsMarried: framePointer SP: stackPointer.
  		 objectMemory storePointer: SuspendedContextIndex ofObject: newProc withValue: activeContext].
  
+ 	newProcOwnerIndex < 0
+ 		ifTrue:
+ 			[self assert: newProcOwnerIndex negated = cogThreadManager getVMOwner.
+ 			 vmThread := cogThreadManager ensureWillingThread.
+ 			 self deny: vmThread index = cogThreadManager getVMOwner.
+ 			 self assert: (cogThreadManager threadIndex: vmThread index isCompatibleWith: newProcOwnerIndex)]
+ 		ifFalse:
+ 			[vmThread := cogThreadManager vmThreadAt: newProcOwnerIndex.
+ 			 vmThread priority: (self quickFetchInteger: PriorityIndex ofObject: newProc).
+ 			 vmThread state = CTMUnavailable ifTrue:
+ 				[vmThread state: CTMWantingOwnership]].
- 	 vmThread := cogThreadManager vmThreadAt: newProcThreadId.
- 	 vmThread priority: (self quickFetchInteger: PriorityIndex ofObject: newProc).
- 	 vmThread state = CTMUnavailable ifTrue:
- 		[vmThread state: CTMWantingOwnership].
  	 self returnToSchedulingLoopAndReleaseVMOrWakeThread: vmThread source: CSSwitchIfNeccessary!

Item was changed:
  ----- Method: CoInterpreterMT>>transferTo:from: (in category 'process primitive support') -----
  transferTo: newProc from: sourceCode
  	"Record a process to be awoken on the next interpreter cycle.  Override to
  	 potentially switch threads either if the new process is bound to another thread,
  	 or if there is no runnable process but there is a waiting thread. Note that the
  	 abort on no runnable process has beeen moved here from wakeHighestPriority."
  	| sched oldProc activeContext |
  	<inline: false>
  	statProcessSwitch := statProcessSwitch + 1.
  	self push: instructionPointer.
  	self externalWriteBackHeadFramePointers.
  	self assertValidExecutionPointe: instructionPointer r: framePointer s: stackPointer.
  	"ensureMethodIsCogged: in makeBaseFrameFor: in
  	 externalSetStackPageAndPointersForSuspendedContextOfProcess:
  	 below may do a code compaction. Nil instructionPointer to avoid it getting pushed twice."
  	instructionPointer := 0.
  	sched := self schedulerPointer.
  	oldProc := objectMemory fetchPointer: ActiveProcessIndex ofObject: sched.
  	self recordContextSwitchFrom: oldProc in: sourceCode.
  	activeContext := self ensureFrameIsMarried: framePointer SP: stackPointer + objectMemory wordSize.
  	objectMemory storePointer: SuspendedContextIndex ofObject: oldProc withValue: activeContext.
  
  	newProc ifNil:
  		["Two possibilities.  One, there is at least one thread waiting to own the VM in which
  		  case it should be activated.  Two, there are no processes to run and so abort."
  		 self willingVMThread ifNotNil:
  			 [:vmThread|
  			  vmThread state = CTMWantingOwnership ifTrue:
  				[self returnToSchedulingLoopAndReleaseVMOrWakeThread: vmThread source: sourceCode]].
  		 self error: 'scheduler could not find a runnable process'].
  
+ 	"Switch to the new process"
  	objectMemory
  		storePointer: ActiveProcessIndex ofObject: sched withValue: newProc;
  		storePointerUnchecked: MyListIndex ofObject: newProc withValue: objectMemory nilObject.
+ 	self externalSetStackPageAndPointersForSuspendedContextOfProcess: newProc.
+ 	"Finally thread switch if required"
+ 	self threadSwitchIfNecessary: newProc from: sourceCode!
- 
- 	self threadSwitchIfNecessary: newProc from: sourceCode.
- 
- 	self externalSetStackPageAndPointersForSuspendedContextOfProcess: newProc!

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 releaseVM.
  		 ^nil].
  	ownerIndex := self ownerIndexOfProcess: activeProc.
+ 	(ownerIndex = 0 or: [cogThreadManager vmOwnerIsCompatibleWith: ownerIndex]) ifTrue:
- 	(ownerIndex = 0 or: [cogThreadManager vmOwnerIs: 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>>assertValidProcessorStackPointersForIndex: (in category 'simulation') -----
+ assertValidProcessorStackPointersForIndex: threadIndex
+ 	| time range |
+ 	time := Time utcMicrosecondClock.
+ 	range := coInterpreter cStackRangeForThreadIndex: threadIndex.
+ 	self assert: ((range includes: cogit processor fp) and: [range includes: cogit processor sp])
+ 
+ 	"(0 to: numThreads + numThreadsIncrement)
+ 		detect:
+ 			[:i| | range |
+ 			range := coInterpreter cStackRangeForThreadIndex: threadIndex.
+ 			((range includes: cogit processor fp)
+ 			and: [range includes: cogit processor sp])]
+ 		ifNone: []"
+ 
+ 	"{ coInterpreter whereIs: cogit processor fp.
+ 		coInterpreter whereIs: cogit processor sp }"!

Item was added:
+ ----- Method: CogThreadManager>>assertValidStackPointersInState:forIndex: (in category 'simulation') -----
+ assertValidStackPointersInState: registerState forIndex: threadIndex
+ 	| time range |
+ 	time := Time utcMicrosecondClock.
+ 	range := coInterpreter cStackRangeForThreadIndex: threadIndex.
+ 	self assert: ((range includes: (registerState at: cogit processor registerStateFPIndex))
+ 				and: [range includes: (registerState at: cogit processor registerStateSPIndex)])
+ 
+ 	"(0 to: numThreads + numThreadsIncrement)
+ 		detect:
+ 			[:i| | range |
+ 			range := coInterpreter cStackRangeForThreadIndex: threadIndex.
+ 			((range includes: (registerState at: cogit processor registerStateFPIndex))
+ 			and: [range includes: (registerState at: cogit processor registerStateSPIndex)])]
+ 		ifNone: []"
+ 
+ 	"{ coInterpreter whereIs: (registerState at: cogit processor registerStateFPIndex).
+ 		coInterpreter whereIs: (registerState at: cogit processor registerStateSPIndex) }"!

Item was changed:
  ----- Method: CogThreadManager>>ensureInitializedProcessor:forThreadIndex: (in category 'simulation') -----
  ensureInitializedProcessor: aProcessor forThreadIndex: threadIndex
  	"Ensure aProcessor has stack pointers within its defined range, initializing it if not."
  	<doNotGenerate>
  	| range |
  	range := coInterpreter cStackRangeForThreadIndex: threadIndex.
  	self assert: (range includes: aProcessor fp) = (range includes: aProcessor sp).
  	((range includes: aProcessor fp) and: [range includes: aProcessor sp]) ifFalse:
  		[aProcessor smashCallerSavedRegistersWithValuesFrom: 16r90000000 by: coInterpreter objectMemory wordSize / 2.
+ 		 cogit initializeProcessorStack: range last.
+ 		 self assertValidProcessorStackPointersForIndex: threadIndex]!
- 		 cogit initializeProcessorStack: range last]!

Item was added:
+ ----- Method: CogThreadManager>>ensureWillingThread (in category 'scheduling') -----
+ ensureWillingThread
+ 	| willingThread newIndex |
+ 	willingThread := self willingVMThread.
+ 	willingThread ifNotNil:
+ 		[^willingThread].
+ 	1 to: numThreads do:
+ 		[:index|
+ 		(self vmThreadAt: index) state ifNil:
+ 			[self startThreadForThreadIndex: index.
+ 			^self vmThreadAt: index]].
+ 	self startThreadForThreadIndex: (newIndex :=  numThreads + 1).
+ 	^self vmThreadAt: newIndex!

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."
  	[aSemaphorePtr value waitTimeoutMSecs: 1000] whileTrue:
  		[coInterpreter checkVMOwnershipFromHeartbeat].
  	self deny: vmOwner = 0.
+ 	cogit withProcessorHaltedDo:
+ 		[| processor |
+ 		processor := cogit processor.
+ 		registerStates
+ 			at: vmOwner
+ 			ifPresent:
+ 				[:registerState|
+ 				self assertValidStackPointersInState: registerState forIndex: vmOwner].
+ 		processor setRegisterState: (registerStates
+ 										at: vmOwner
- 	cogit processor setRegisterState: (registerStates at: vmOwner
  										ifAbsentPut:
+ 											[self ensureInitializedProcessor: processor forThreadIndex: vmOwner.
+ 											 processor registerState])]!
- 											[self ensureInitializedProcessor: cogit processor forThreadIndex: vmOwner.
- 											 cogit processor registerState])!

Item was added:
+ ----- Method: CogThreadManager>>registerStates (in category 'simulation') -----
+ registerStates
+ 	<doNotGenerate>
+ 	^registerStates!

Item was changed:
  ----- Method: CogThreadManager>>releaseVM (in category 'public api') -----
  releaseVM
+ 	<inline: #always>
- 	"In the simulation this is where register state is saved; it is switched in tryLockVMOwnerTo:."
- 	self cCode: [] inSmalltalk:
- 		[vmOwner ~= 0 ifTrue:
- 			[registerStates at: vmOwner put: cogit processor registerState]].
  	self setVMOwner: 0!

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 cCode: '' inSmalltalk:
+ 		[coInterpreter transcript
+ 			ensureCr;
+ 			f: 'setVMOwner: %d -> %d (%s)\n'
+ 			printf: { vmOwner. indexOrZero. thisContext home sender selector }.
+ 		"In the simulation this is where register state is saved; it is switched here and in tryLockVMOwnerTo:."
+ 		(vmOwner ~= 0 and: [vmOwner ~= indexOrZero]) ifTrue:
+ 			[self assertValidProcessorStackPointersForIndex: vmOwner.
+ 			 registerStates at: vmOwner put: cogit processor registerState].
+ 		(registerStates at: indexOrZero ifPresent: [:registerState| cogit processor setRegisterState: registerState])].
  	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
+ 		ifTrue: [aThreadIndex = processThreadId]
+ 		ifFalse: [aThreadIndex ~= processThreadId negated]!
- 		ifTrue: [aThreadIndex = (processThreadId >> 1)]
- 		ifFalse: [aThreadIndex ~= (0 - (processThreadId >> 1))]!

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 saved and switched, simulating a thread
+ 	 switch. setVMOwner: also saves register state.  The code here and in setVMOwner: allow
+ 	 us to avoid the expensive and complex MultiProcessor hack."
- 	 In the simulation this is where register state is saved and switched, simulaitng a thread switch.
- 	 releaseVM also saves register state.  The code here and in registerState allow us to avoid the
- 	 expensive and complex MultiProcessor hack."
  	<doNotGenerate>
  	| result |
  	self deny: threadIndex = 0.
  	cogit withProcessorHaltedDo:
+ 		[| currentOwner prior processor  |
- 		[| prior processor  |
  		processor := cogit processor.
  		prior := processor registerState.
  		"A thread switch would (have) occur(ed) if it were that the VM were owned other than by threadIndex"
+ 		(currentOwner := vmOwner) ~= threadIndex ifTrue:
+ 			[currentOwner ~= 0 ifTrue:
+ 				[self assertValidStackPointersInState: prior forIndex: currentOwner.
+ 				registerStates at: currentOwner put: prior].
- 		vmOwner ~= threadIndex ifTrue:
- 			[vmOwner ~= 0 ifTrue:
- 				[registerStates at: vmOwner put: prior].
  			 processor setRegisterState: (registerStates
  											at: threadIndex
  											ifAbsentPut:
  												[self ensureInitializedProcessor: processor forThreadIndex: threadIndex.
  												 processor registerState])].
  		result := cogit tryLockVMOwnerTo: threadIndex.
  		self assert: result = (threadIndex = vmOwner).
+ 		result
+ 			ifTrue: [registerStates at: threadIndex put: processor registerState]
+ 			ifFalse: [processor setRegisterState: prior].
+ 		coInterpreter transcript
+ 			ensureCr;
+ 			f: (result ifTrue: ['tryLockVMOwner %d -> %d (%s) ok\n'] ifFalse: ['tryLockVMOwner %d -> %d (%s) FAILED\n'])
+ 			printf: { currentOwner. threadIndex. thisContext home sender selector }.
+ 		self assertValidProcessorStackPointersForIndex: vmOwner].
- 		registerStates at: threadIndex put: processor registerState.
- 		threadIndex ~= vmOwner ifTrue: "the lock attempt failed; undo the (processor) thread switch."
- 			[processor setRegisterState: prior]].
  	^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.
  	vmThread state
  		ifNil: [self startThreadForThreadInfo: vmThread]
  		ifNotNil:
+ 			[self assert: (vmThread state = CTMWantingOwnership
+ 						or: [vmThread state = CTMAssignableOrInVM]).
- 			[self assert: vmThread state = CTMWantingOwnership.
  			 self ioSignalOSSemaphore: (self addressOf: vmThread osSemaphore)].
  	self ioTransferTimeslice!

Item was changed:
  ----- Method: CogThreadManager>>willingVMThread (in category 'thread set') -----
  willingVMThread
  	"Answer a pointer to a live CogVMThread in any of the ``will do VM work''
  	 states (other than the current owner if the VM is owned), or nil if none.
  	 Preferentially answer threads wanting ownership."
  	<returnTypeC: #'CogVMThread *'>
  	| thread threadWantingVM threadWilling |
  	<inline: false>
  	threadWantingVM := threadWilling := nil.
  	1 to: numThreads do:
  		[:i|
  		 (self vmOwnerIs: i) ifFalse:
  			[thread := threads at: i.
+ 			 thread state = CTMWantingOwnership ifTrue:
- 			 thread state =  CTMWantingOwnership ifTrue:
  				[(threadWantingVM isNil
  				  or: [threadWantingVM priority < thread priority]) ifTrue:
  					[threadWantingVM := thread]].
+ 			 thread state = CTMAssignableOrInVM ifTrue:
- 			 thread state =  CTMAssignableOrInVM ifTrue:
  				[(threadWilling isNil
  				  or: [threadWilling priority < thread priority]) ifTrue:
  					[threadWilling := thread]]]].
  	^threadWantingVM ifNil:
  		[threadWilling]!

Item was added:
+ ----- Method: Exception>>reset (in category '*VMMaker-reuse') -----
+ reset
+ 	"Allow the receiver to be resignalled"
+ 	signalContext := nil!



More information about the Vm-dev mailing list