Hi all, Hi Marcel,

 apologies for breaking the VM build in recent commits.  I shall have things fixed before Monday.  Bear with me.

On Wed, Apr 17, 2024 at 4:17 PM <commits@source.squeak.org> wrote:
 
Eliot Miranda uploaded a new version of VMMaker to project VM Maker:
http://source.squeak.org/VMMaker/VMMaker.oscog-eem.3365.mcz

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

Name: VMMaker.oscog-eem.3365
Author: eem
Time: 17 April 2024, 4:17:09.846667 pm
UUID: d067e715-4de3-4931-8c6e-5884b5fbab91
Ancestors: VMMaker.oscog-eem.3364

First pass at getting threaded VM to work on Mac. Only call ioProcessEvents if teh platform doesn't care (doesn't define ioEventThreadAffinity, or defines it as 0) or the current thread id is the same as ioEventThreadAffinity.

Nuke vmOwnerAddress & vmOwnerFromMachineCode which were used only by the pre-C-atomics access code to generate the machine code accessors for vmOwner.

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

Item was changed:
  ----- Method: CoInterpreterMT>>checkForEventsMayContextSwitch: (in category 'process primitive support') -----
  checkForEventsMayContextSwitch: mayContextSwitch
        "Check for possible interrupts and handle one if necessary.
         Answer if a context switch has occurred."
+       | switched sema now threadingThwartsIOProcessEventsInvocation |
-       | switched sema now |
        <inline: false>
        <var: #now type: #usqLong>
        self assertSaneThreadAndProcess.
        cogit assertCStackWellAligned.
        statCheckForEvents := statCheckForEvents + 1.

        "restore the stackLimit if it has been smashed."
        self restoreStackLimit.
        self externalWriteBackHeadFramePointers.
        self assert: stackPage = stackPages mostRecentlyUsedPage.

        "Allow the platform to do anything it needs to do synchronously."
        self ioSynchronousCheckForEvents.

        self checkCogCompiledCodeCompactionCalledFor.

        objectMemory needGCFlag ifTrue:
                ["sufficientSpaceAfterGC: runs the incremental GC and
                 then, if not enough space is available, the fullGC."
                 (objectMemory sufficientSpaceAfterGC: 0) ifFalse:
                        [self setSignalLowSpaceFlagAndSaveProcess]].

        mayContextSwitch ifFalse: [^false].

        switched := false.
        self assert: deferThreadSwitch not.
        deferThreadSwitch := true.

        (profileProcess ~= objectMemory nilObject
         or: [nextProfileTick > 0 and:[self ioHighResClock >= nextProfileTick]]) ifTrue:
                [self zeroNextProfileTick.
                 "Take a sample (if not already done so) for the profiler if it is active.  This
                  must be done before any of the synchronousSignals below or else we will
                  attribute a pause in ioRelinquishProcessor to the newly activated process."
                 profileProcess = objectMemory nilObject ifTrue:
                        [profileProcess := self activeProcess.
                         profileMethod := objectMemory nilObject].
                 "and signal the profiler semaphore if it is present"
                 (profileSemaphore ~= objectMemory nilObject
                  and: [self synchronousSignal: profileSemaphore]) ifTrue:
                        [switched := true]].

        self cppIf: #LRPCheck
                ifTrue:
                        [self checkDeliveryOfLongRunningPrimitiveSignal ifTrue:
                                [switched := true]].

        objectMemory signalLowSpace ifTrue:
                [objectMemory signalLowSpace: false. "reset flag"
                 sema := objectMemory splObj: TheLowSpaceSemaphore.
                 (sema ~= objectMemory nilObject
                  and: [self synchronousSignal: sema]) ifTrue:
                        [switched := true]].

+       threadingThwartsIOProcessEventsInvocation := self checkInvokeIOProcessEvents: (now := self ioUTCMicroseconds).
-       "inIOProcessEvents prevents reentrancy into ioProcessEvents and allows disabling
-        ioProcessEvents e.g. for native GUIs.  We would like to manage that here but can't
-        since the platform code may choose to call ioProcessEvents itself in various places."
-       false
-               ifTrue:
-                       [((now := self ioUTCMicroseconds) >= nextPollUsecs
-                        and: [inIOProcessEvents = 0]) ifTrue:
-                               [statIOProcessEvents := statIOProcessEvents + 1.
-                                inIOProcessEvents := inIOProcessEvents + 1.
-                                self ioProcessEvents. "sets interruptPending if interrupt key pressed; may callback"
-                                inIOProcessEvents > 0 ifTrue:
-                                       [inIOProcessEvents := inIOProcessEvents - 1].
-                                nextPollUsecs := now + 20000
-                                "msecs to wait before next call to ioProcessEvents.  Note that strictly
-                                 speaking we might need to update 'now' at this point since
-                                 ioProcessEvents could take a very long time on some platforms"]]
-               ifFalse:
-                       [(now := self ioUTCMicroseconds) >= nextPollUsecs ifTrue:
-                               [statIOProcessEvents := statIOProcessEvents + 1.
-                                self ioProcessEvents. "sets interruptPending if interrupt key pressed; may callback"
-                                nextPollUsecs := now + 20000
-                                "msecs to wait before next call to ioProcessEvents.  Note that strictly
-                                 speaking we might need to update 'now' at this point since
-                                 ioProcessEvents could take a very long time on some platforms"]].

        interruptPending ifTrue:
                [interruptPending := false.
                 "reset interrupt flag"
                 sema := objectMemory splObj: TheInterruptSemaphore.
                 (sema ~= objectMemory nilObject
                  and: [self synchronousSignal: sema]) ifTrue:
                        [switched := true]].

        nextWakeupUsecs ~= 0 ifTrue:
                [now >= nextWakeupUsecs ifTrue:
                        [nextWakeupUsecs := 0.
                         "set timer interrupt to 0 for 'no timer'"
                         sema := objectMemory splObj: TheTimerSemaphore.
                         (sema ~= objectMemory nilObject
                          and: [self synchronousSignal: sema]) ifTrue:
                                [switched := true]]].

        "signal any pending finalizations"
        pendingFinalizationSignals > 0 ifTrue:
                [pendingFinalizationSignals := 0.
                 sema := objectMemory splObj: TheFinalizationSemaphore.
                 (sema ~= objectMemory nilObject
                  and: [self synchronousSignal: sema]) ifTrue:
                        [switched := true]].

        "signal all semaphores in semaphoresToSignal"
        self signalExternalSemaphores ifTrue:
                [switched := true].

        deferThreadSwitch := false.
+       (threadingThwartsIOProcessEventsInvocation
+       or: [checkThreadActivation]) ifTrue:
-       checkThreadActivation ifTrue:
                [checkThreadActivation := false.
                 self cedeToHigherPriorityThreads]. "N.B.  This may not return if we do switch."

        self threadSwitchIfNecessary: self activeProcess from: CSCheckEvents.
        ^switched!

Item was added:
+ ----- Method: CoInterpreterMT>>checkInvokeIOProcessEvents: (in category 'process primitive support') -----
+ checkInvokeIOProcessEvents: now
+       "Some platforms allow event resaponse only from a subset of threads (e.g. on Mac, the main application thread).
+        Override to check if the platform allows us to invoke or not and answer true if ioProcessEvents should have been
+        invoked but wasn't because of thread affinity."
+       <inline: #always>
+       (self ioEventThreadAffinity > 0
+       and: [self ioEventThreadAffinity ~= cogThreadManager getVMOwner]) ifTrue:
+               [^now >= nextPollUsecs].
+       super checkInvokeIOProcessEvents: now.
+       ^false!

Item was removed:
- ----- Method: CoInterpreterMT>>vmOwnerAddress (in category 'simulation') -----
- vmOwnerAddress
-       <doNotGenerate>
-       ^cogThreadManager vmOwnerAddress!

Item was removed:
- ----- Method: CogThreadManager>>vmOwnerAddress (in category 'Cogit lock implementation') -----
- vmOwnerAddress
-       <api> "NB. For the JIT only, so it can generate the lock & unlock functions."
-       <returnTypeC: #usqInt>
-       self error: 'Deprecated!! Replaced by proper atomic functions'.
-       ^self
-               cCode: [(self addressOf: vmOwner) asUnsignedInteger]
-               inSmalltalk: [cogit simulatedReadWriteVariableAddress: #vmOwnerFromMachineCode in: self]!

Item was removed:
- ----- Method: CogThreadManager>>vmOwnerFromMachineCode (in category 'Cogit lock implementation') -----
- vmOwnerFromMachineCode
-       <doNotGenerate>
-       self error: 'Deprecated!! Replaced by proper atomic C functions'.
-       ^vmOwner!

Item was removed:
- ----- Method: CogThreadManager>>vmOwnerFromMachineCode: (in category 'Cogit lock implementation') -----
- vmOwnerFromMachineCode: aValue
-       <doNotGenerate>
-       self error: 'Deprecated!! Replaced by proper atomic C functions'.
-       self assert: (aValue between: 0 and: numThreads).
-       vmOwner := aValue!

Item was changed:
  ----- Method: StackInterpreter>>checkForEventsMayContextSwitch: (in category 'process primitive support') -----
  checkForEventsMayContextSwitch: mayContextSwitch
        "Check for possible interrupts and handle one if necessary.
         Answer if a context switch has occurred."
        | switched sema now |
        <inline: false>
        <var: #now type: #usqLong>
        statCheckForEvents := statCheckForEvents + 1.

        "restore the stackLimit if it has been smashed."
        self restoreStackLimit.
        self externalWriteBackHeadFramePointers.
        self assert: stackPage = stackPages mostRecentlyUsedPage.

        "Allow the platform to do anything it needs to do synchronously."
        self ioSynchronousCheckForEvents.

        self checkCogCompiledCodeCompactionCalledFor.

        objectMemory needGCFlag ifTrue:
                ["sufficientSpaceAfterGC: runs the incremental GC and
                 then, if not enough space is available, the fullGC."
                 (objectMemory sufficientSpaceAfterGC: 0) ifFalse:
                        [self setSignalLowSpaceFlagAndSaveProcess]].

        mayContextSwitch ifFalse: [^false].

        switched := false.

        (profileProcess ~= objectMemory nilObject
         or: [nextProfileTick > 0 and:[self ioHighResClock >= nextProfileTick]]) ifTrue:
                [self zeroNextProfileTick.
                 "Take a sample (if not already done so) for the profiler if it is active.  This
                  must be done before any of the synchronousSignals below or else we will
                  attribute a pause in ioRelinquishProcessor to the newly activated process."
                 profileProcess = objectMemory nilObject ifTrue:
                        [profileProcess := self activeProcess.
                         profileMethod := objectMemory nilObject].
                 "and signal the profiler semaphore if it is present"
                 (profileSemaphore ~= objectMemory nilObject
                  and: [self synchronousSignal: profileSemaphore]) ifTrue:
                        [switched := true]].

        self cppIf: #LRPCheck
                ifTrue:
                        [self checkDeliveryOfLongRunningPrimitiveSignal ifTrue:
                                [switched := true]].

        objectMemory signalLowSpace ifTrue:
                [objectMemory signalLowSpace: false. "reset flag"
                 sema := objectMemory splObj: TheLowSpaceSemaphore.
                 (sema ~= objectMemory nilObject
                  and: [self synchronousSignal: sema]) ifTrue:
                        [switched := true]].

+       self checkInvokeIOProcessEvents: (now := self ioUTCMicroseconds).
-       "inIOProcessEvents prevents reentrancy into ioProcessEvents and allows disabling
-        ioProcessEvents e.g. for native GUIs.  We would like to manage that here but can't
-        since the platform code may choose to call ioProcessEvents itself in various places."
-       false
-               ifTrue:
-                       [((now := self ioUTCMicroseconds) >= nextPollUsecs
-                        and: [inIOProcessEvents = 0]) ifTrue:
-                               [statIOProcessEvents := statIOProcessEvents + 1.
-                                inIOProcessEvents := inIOProcessEvents + 1.
-                                self ioProcessEvents. "sets interruptPending if interrupt key pressed; may callback"
-                                inIOProcessEvents > 0 ifTrue:
-                                       [inIOProcessEvents := inIOProcessEvents - 1].
-                                nextPollUsecs := now + 20000
-                                "msecs to wait before next call to ioProcessEvents.  Note that strictly
-                                 speaking we might need to update 'now' at this point since
-                                 ioProcessEvents could take a very long time on some platforms"]]
-               ifFalse:
-                       [(now := self ioUTCMicroseconds) >= nextPollUsecs ifTrue:
-                               [statIOProcessEvents := statIOProcessEvents + 1.
-                                self ioProcessEvents. "sets interruptPending if interrupt key pressed; may callback"
-                                nextPollUsecs := now + 20000
-                                "msecs to wait before next call to ioProcessEvents.  Note that strictly
-                                 speaking we might need to update 'now' at this point since
-                                 ioProcessEvents could take a very long time on some platforms"]].

        interruptPending ifTrue:
                [interruptPending := false.
                 "reset interrupt flag"
                 sema := objectMemory splObj: TheInterruptSemaphore.
                 (sema ~= objectMemory nilObject
                  and: [self synchronousSignal: sema]) ifTrue:
                        [switched := true]].

        nextWakeupUsecs ~= 0 ifTrue:
                [now >= nextWakeupUsecs ifTrue:
                        [nextWakeupUsecs := 0.
                         "set timer interrupt to 0 for 'no timer'"
                         sema := objectMemory splObj: TheTimerSemaphore.
                         (sema ~= objectMemory nilObject
                          and: [self synchronousSignal: sema]) ifTrue:
                                [switched := true]]].

        "signal any pending finalizations"
        pendingFinalizationSignals > 0 ifTrue:
                [pendingFinalizationSignals := 0.
                 sema := objectMemory splObj: TheFinalizationSemaphore.
                 (sema ~= objectMemory nilObject
                  and: [self synchronousSignal: sema]) ifTrue:
                        [switched := true]].

        "signal all semaphores in semaphoresToSignal"
        self signalExternalSemaphores ifTrue:
                [switched := true].

        ^switched!

Item was added:
+ ----- Method: StackInterpreter>>checkInvokeIOProcessEvents: (in category 'process primitive support') -----
+ checkInvokeIOProcessEvents: now
+       "inIOProcessEvents prevents reentrancy into ioProcessEvents and allows disabling
+        ioProcessEvents e.g. for native GUIs.  We would like to manage that here but can't
+        since the platform code may choose to call ioProcessEvents itself in various places."
+       <inline: #always>
+       false
+               ifTrue:
+                       [(now >= nextPollUsecs
+                        and: [inIOProcessEvents = 0]) ifTrue:
+                               [statIOProcessEvents := statIOProcessEvents + 1.
+                                inIOProcessEvents := inIOProcessEvents + 1.
+                                self ioProcessEvents. "sets interruptPending if interrupt key pressed; may callback"
+                                inIOProcessEvents > 0 ifTrue:
+                                       [inIOProcessEvents := inIOProcessEvents - 1].
+                                nextPollUsecs := now + 20000
+                                "msecs to wait before next call to ioProcessEvents.  Note that strictly
+                                 speaking we might need to update 'now' at this point since
+                                 ioProcessEvents could take a very long time on some platforms"]]
+               ifFalse:
+                       [now >= nextPollUsecs ifTrue:
+                               [statIOProcessEvents := statIOProcessEvents + 1.
+                                self ioProcessEvents. "sets interruptPending if interrupt key pressed; may callback"
+                                nextPollUsecs := now + 20000
+                                "msecs to wait before next call to ioProcessEvents.  Note that strictly
+                                 speaking we might need to update 'now' at this point since
+                                 ioProcessEvents could take a very long time on some platforms"]].!



--
_,,,^..^,,,_
best, Eliot