<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">&lt;<a href="mailto:craig@netjam.org" target="_blank">craig@netjam.org</a>&gt;</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>
&gt; I guess the system is never arranging to unlock the Vm so there is<br>
&gt; never any opportunity for the foreign callback to get entry.  Try<br>
&gt; adding an occasional &quot;Processor relinquishProcessorForMicroseconds:<br>
&gt; 100&quot; 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&#39;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&#39;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.
        &quot;If the current thread doesn&#39;t have an index it&#39;s new to the vm
         and we need to allocate a new threadInfo, failing if we can&#39;t.
         We also need a process in the foreignCallbackProcessSlot upon
         which to run the thread&#39;s eventual callback.&quot;
        [[cogThreadManager tryLockVMToIndex: -1] whileFalse:
                [self waitingPriorityIsAtLeast: foreignCallbackPriority. 
                cogThreadManager ioTransferTimeslice].
         (objectMemory splObj: foreignCallbackProcessSlot) ~= objectMemory nilObject] whileFalse:
                [cogThreadManager releaseVM.
                 (count := count + 1) &gt; 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
        &quot;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.&quot;
        maxWaitingPriority &lt; 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
        &quot;Check for possible interrupts and handle one if necessary.
         Answer if a context switch has occurred.&quot;
        ...

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

        ...

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

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

        nextWakeupUsecs ~= 0 ifTrue:
                [now &gt;= nextWakeupUsecs ifTrue:
                        [nextWakeupUsecs := 0.
                         &quot;set timer interrupt to 0 for &#39;no timer&#39;&quot;
                         sema := objectMemory splObj: TheTimerSemaphore.
                         (sema ~= objectMemory nilObject
                          and: [self synchronousSignal: sema]) ifTrue:
                                [switched := true]]].

        ...

        deferThreadSwitch := false.
        checkThreadActivation ifTrue:
                [checkThreadActivation := false.
                 self cedeToHigherPriorityThreads]. &quot;N.B.  This may not return if we do switch.&quot;

        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&#39;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&#39;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>&quot;Invoked from transferTo:from: to switch threads if the new process is bound or affined to some other thread.&quot;</div><div><span class="" style="white-space:pre">        ....</span></div><div><span class="" style="white-space:pre">        </span>(self quickFetchInteger: PriorityIndex ofObject: newProc) &lt; 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) &lt; 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&#39;s a while since I thought about this and I&#39;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>