<div dir="ltr"><br><div class="gmail_extra"><br><div class="gmail_quote">On Thu, Oct 8, 2015 at 3:43 PM, Craig Latta <span dir="ltr"><<a href="mailto:craig@netjam.org" target="_blank">craig@netjam.org</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-color:rgb(204,204,204);border-left-style:solid;padding-left:1ex"><span class=""><br>
<br>
> I guess the system is never arranging to unlock the Vm so there is<br>
> never any opportunity for the foreign callback to get entry. Try<br>
> adding an occasional "Processor relinquishProcessorForMicroseconds:<br>
> 100" and see if that gives your call-back a window.<br>
<br>
</span> Well, my system is deadlocked in the foreign thread, forever trying<br>
and failing to own the VM when the library I'm using attempts to invoke<br>
my callback. I guess I have to do something more drastic, like kludge<br>
the unlock logic.<br>
<br>
How should the system be arranging to unlock the VM?<br></blockquote><div><br></div><div>Hmmm, the code is better than I thought. So here's what it does:</div><div><br></div><div>ownVMFromUnidentifiedThread<br></div><div><span class="" style="white-space:pre">        ...</span><br></div><div><span class="" style="white-space:pre">        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 tryLockVMToIndex: -1] whileFalse:
                [self waitingPriorityIsAtLeast: foreignCallbackPriority.
                cogThreadManager ioTransferTimeslice].
         (objectMemory splObj: foreignCallbackProcessSlot) ~= objectMemory nilObject] whileFalse:
                [cogThreadManager releaseVM.
                 (count := count + 1) > 1000 ifTrue:
                        [^-2].
                 cogThreadManager ioMilliSleep: 1].
</span></div><div><span class="" style="white-space:pre"><br></span></div><div><span class="" style="white-space:pre">and</span></div><div><span class="" style="white-space:pre"><br></span></div><div><span class="" style="white-space:pre">waitingPriorityIsAtLeast: minPriority
        "Set the maxWaitingPriority to at least minPriority on behalf
         of a thread wanting to acquire the VM. If maxWaitingPriority
         is increased, schedule a thread activation check asap."
        maxWaitingPriority < minPriority ifTrue:
                [maxWaitingPriority := minPriority.
                 checkThreadActivation := true.
                 self forceInterruptCheck]<br></span></div><div><span class="" style="white-space:pre"><br></span></div><div><span class="" style="white-space:pre">So forceInterruptCheck will force the VM to break out of machine code, if it is in machine code (this we need to check; for example it might be doing relinquishProcessorForMicroseconds:). Once in checkForEventsMayContextSwitch (the code that gets called to process events) there is an attempt to attend to checkThreadActivation:</span></div><div><span class="" style="white-space:pre"><br></span></div><div><span class="" style="white-space:pre">checkForEventsMayContextSwitch: mayContextSwitch
        "Check for possible interrupts and handle one if necessary.
         Answer if a context switch has occurred."
        ...
        switched := false.
        self assert: deferThreadSwitch not.
        deferThreadSwitch := true.
        ...
        "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."
        (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]]].
        ...
        deferThreadSwitch := false.
        checkThreadActivation ifTrue:
                [checkThreadActivation := false.
                 self cedeToHigherPriorityThreads]. "N.B. This may not return if we do switch."
        self threadSwitchIfNecessary: self activeProcess from: CSCheckEvents.
        ^switched<br></span></div><div><span class="" style="white-space:pre"><br></span></div><div><span class="" style="white-space:pre">So </span><span style="white-space:pre">cedeToHigherPriorityThreads should do what it says. What does it do?</span></div><div><span style="white-space:pre"><br></span></div><div><span style="white-space:pre">Well it does lots of clever things, but AFAICT it *doesn't* unlock the VM if a higher-priority foreign callback is trying to own the VM. So that would be the bug.</span></div><div><span style="white-space:pre"><br></span></div><div><span style="white-space:pre">If you want to make progress on this you'll need to</span></div><div><span style="white-space:pre"><br></span></div><div><span style="white-space:pre">a) build a simulator image (instructions on the blog)</span></div><div><span style="white-space:pre">b) set-up a test case in the simulator to exercise the </span><span style="white-space:pre">cedeToHigherPriorityThreads logic and replicate the bug</span></div><div><span style="white-space:pre">c) have a go at rewriting </span><span style="white-space:pre">cedeToHigherPriorityThreads and/or </span>threadSwitchIfNecessary:from: to do the right thing.</div><div><br></div><div>Looks like the issue is at the bottom of threadSwitchIfNecessary:from: :</div><div><br></div><div><div>threadSwitchIfNecessary: newProc from: sourceCode</div><div><span class="" style="white-space:pre">        </span>"Invoked from transferTo:from: to switch threads if the new process is bound or affined to some other thread."</div><div><span class="" style="white-space:pre">        ....</span></div><div><span class="" style="white-space:pre">        </span>(self quickFetchInteger: PriorityIndex ofObject: newProc) < maxWaitingPriority ifTrue:</div><div><span class="" style="white-space:pre">                </span>[checkThreadActivation := true.</div><div><span class="" style="white-space:pre">                </span> self forceInterruptCheck]</div></div><div><br></div><div>This will just spin causing the VM to do checkForEventsMayContextSwitch:.</div><div><br></div><div>Instead it probably needs to do something like</div><div><br></div><div><div><span class="" style="white-space:pre">        </span>(self quickFetchInteger: PriorityIndex ofObject: newProc) < maxWaitingPriority ifTrue:</div><div><span class="" style="white-space:pre">                </span>[checkThreadActivation := true.</div><div><span class="" style="white-space:pre">                </span> self returnToSchedulingLoopAndReleaseVMOrWakeThread: vmThread source: source]</div></div><div><br></div><div>But this really needs to be thought through carefully. It's a while since I thought about this and I'm in no position to do so now.</div><div>Anyway, HTH</div><div><br></div></div><div class="gmail_signature"><div dir="ltr"><div><span style="font-size:small;border-collapse:separate"><div>_,,,^..^,,,_<br></div><div>best, Eliot</div></span></div></div></div>
</div></div>