<div dir="ltr">Hi Craig,<div><br></div><div>    first, the message from the non-MT VM, &quot;Warning; callback failed to own the VM&quot;, indicates that a callback is coming in on other than the VM thread.  Here&#39;s the non-MT implementation from sqVirtualMachine.c:</div><div><br></div><div><div>sqInt ownVM(sqInt threadIdAndFlags)</div><div>{</div><div>    extern sqInt amInVMThread(void);</div><div>    return amInVMThread() ? 0 : -1;</div><div>}</div></div><div><br></div><div><br></div><div>So with the normal VM any callbacks must come in on the thread that made a callout.</div><div class="gmail_extra"><br><div class="gmail_quote">On Tue, Oct 6, 2015 at 7:23 AM, 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"><br>
     Hm, I still get the &quot;failed to own the VM&quot; message with the<br>
multithreaded VM. I thought it would wait until it could run the<br>
callback from a VM thread? What now?<br></blockquote><div><br></div><div>Second, the MT VM /is/ only a prototype.  But second, what&#39;s the platform, etc?  Do you have a reproducible case?  Are you willing to use gdb et al to debug ownVM?</div><div><br></div><div>The issue here is when a callback comes in from some unknown thread, what do we do with it?  By default it is rejected.  But if there is a process in the foreignCallbackProcessSlot of the specialObjectsArray, it will be cloned to handle each &quot;foreign&quot; callback.  Likely you don&#39;t have such a process installed, hence the callbacks getting rejected.<br></div><div><br></div><div><br></div><div>If you read CogInterpreterMT&gt;&gt;ownVM: you&#39;ll see</div><div><br></div><div> CogInterpreterMT&gt;&gt;ownVM: threadIndexAndFlags</div><div><span class="" style="white-space:pre">        </span>&lt;api&gt;</div><div><span class="" style="white-space:pre">        </span>&lt;inline: false&gt;</div><div><span class="" style="white-space:pre">        </span>&quot;This is the entry-point for plugins and primitives that wish to reacquire the VM after having</div><div><span class="" style="white-space:pre">        </span> released it via disownVM or callbacks that want to acquire it without knowing their ownership</div><div><span class="" style="white-space:pre">        </span> status.  This call will block until the VM is owned by the current thread or an error occurs.</div><div><span class="" style="white-space:pre">        </span> The argument should be the value answered by disownVM, or 0 for callbacks that don&#39;t know</div><div><span class="" style="white-space:pre">        </span> if they have disowned or not.  This is both an optimization to avoid having to query thread-</div><div><span class="" style="white-space:pre">        </span> local storage for the current thread&#39;s index (since it can easily keep it in some local variable),</div><div><span class="" style="white-space:pre">        </span> and a record of when an unbound process becomes affined to a thread for the dynamic</div><div><span class="" style="white-space:pre">        </span> extent of some operation.</div><div><br></div><div><span class="" style="white-space:pre">        </span> Answer 0 if the current thread is known to the VM.</div><div><span class="" style="white-space:pre">        </span> Answer 1 if the current thread is unknown to the VM and takes ownership.</div><div><span class="" style="white-space:pre">        </span> Answer -1 if the current thread is unknown to the VM and fails to take ownership.&quot;</div><div><span class="" style="white-space:pre">        </span>| threadIndex flags vmThread myProc activeProc sched |</div><div><span class="" style="white-space:pre">        </span>&lt;var: #vmThread type: #&#39;CogVMThread *&#39;&gt;</div><div><span class="" style="white-space:pre">        </span>threadIndexAndFlags = 0 ifTrue:</div><div><span class="" style="white-space:pre">                </span>[^self ownVMFromUnidentifiedThread].</div></div><span style="white-space:pre">        ...</span></div><div class="gmail_extra"><span style="white-space:pre"><br></span></div><div class="gmail_extra"><span style="white-space:pre">and</span></div><div class="gmail_extra">CogInterpreterMT&gt;&gt;ownVMFromUnidentifiedThread<div class="gmail_extra"><span class="" style="white-space:pre">        </span>&quot;Attempt to take ownership from a thread that as yet doesn&#39;t know its index.</div><div class="gmail_extra"><span class="" style="white-space:pre">        </span> This supports callbacks where the callback could originate from any thread.</div><div class="gmail_extra"><span class="" style="white-space:pre">        </span></div><div class="gmail_extra"><span class="" style="white-space:pre">        </span> Answer 0 if the owning thread is known to the VM.</div><div class="gmail_extra"><span class="" style="white-space:pre">        </span> Answer 1 if the owning thread is unknown to the VM and now owns the VM.</div><div class="gmail_extra"><span class="" style="white-space:pre">        </span> Answer -1 if the owning thread is unknown to the VM and fails to own the VM.</div><div class="gmail_extra"><span class="" style="white-space:pre">        </span> Answer -2 if the owning thread is unknown to the VM and there is no foreign callback process installed.&quot;</div><div class="gmail_extra"><span class="" style="white-space:pre">        </span>| count threadIndex vmThread |</div><div class="gmail_extra"><span class="" style="white-space:pre">        </span>&lt;var: #vmThread type: #&#39;CogVMThread *&#39;&gt;</div><div class="gmail_extra"><span class="" style="white-space:pre">        </span>&lt;inline: false&gt;</div><div class="gmail_extra"><span class="" style="white-space:pre">        </span>(threadIndex := cogThreadManager ioGetThreadLocalThreadIndex) ~= 0 ifTrue:</div><div class="gmail_extra"><span class="" style="white-space:pre">                </span>[ &quot;this is a callback from a known thread&quot;</div><div class="gmail_extra"><span class="" style="white-space:pre">                </span> threadIndex = cogThreadManager getVMOwner ifTrue: &quot;the VM has not been disowned&quot;</div><div class="gmail_extra"><span class="" style="white-space:pre">                        </span>[self assert: (disowningVMThread isNil or: [disowningVMThread = self currentVMThread]).</div><div class="gmail_extra"><span class="" style="white-space:pre">                        </span> disowningVMThread := nil.</div><div class="gmail_extra"><span class="" style="white-space:pre">                        </span> self currentVMThread state: CTMAssignableOrInVM.</div><div class="gmail_extra"><span class="" style="white-space:pre">                        </span> ^VMAlreadyOwnedHenceDoNotDisown].</div><div class="gmail_extra"><span class="" style="white-space:pre">                </span> ^self ownVM: threadIndex].</div><div class="gmail_extra"><span class="" style="white-space:pre">        </span>foreignCallbackPriority = 0 ifTrue:</div><div class="gmail_extra"><span class="" style="white-space:pre">                </span>[^-2].</div><div class="gmail_signature"><div dir="ltr"><div><span style="font-size:small;border-collapse:separate"><span style="white-space:pre">        ...</span></span></div><div><span style="white-space:pre"><br></span></div><div><span style="white-space:pre">i.e. that to accept a callback from an unknown thread the system has to have the priority at which</span> to run callbacks from unknown threads (foreignCallbackPriority) determined, and that&#39;s set by the priority of the process filling the foreignCallbackProcessSlot in the specialObjectsArray, as set by disownVM:.</div><div><br></div><div><div>CogInterpreterMT&gt;&gt;disownVM: flags</div><div><span class="" style="white-space:pre">        </span>&quot;Release the VM to other threads and answer the current thread&#39;s index.</div><div><span class="" style="white-space:pre">        </span> Currently valid flags:</div><div><span class="" style="white-space:pre">                </span>DisownVMLockOutFullGC<span class="" style="white-space:pre">        </span>- prevent fullGCs while this thread disowns the VM.</div><div><span class="" style="white-space:pre">                </span>OwnVMForeignThreadFlag<span class="" style="white-space:pre">        </span>- indicates lowest-level entry from a foreign thread</div><div><span class="" style="white-space:pre">                                                                        </span>- not to be used explicitly by clients</div><div><span class="" style="white-space:pre">                                                                        </span>- only set by ownVMFromUnidentifiedThread</div><div><span class="" style="white-space:pre">                </span>VMAlreadyOwnedHenceDoNotDisown</div><div><span class="" style="white-space:pre">                                                                        </span>- indicates an ownVM from a callback was made when</div><div><span class="" style="white-space:pre">                                                                        </span>  the vm was still owned.</div><div><span class="" style="white-space:pre">                                                                        </span>- not to be used explicitly by clients</div><div><span class="" style="white-space:pre">                                                                        </span>- only set by ownVMFromUnidentifiedThread</div><div><br></div><div><span class="" style="white-space:pre">        </span> This is the entry-point for plugins and primitives that wish to release the VM while</div><div><span class="" style="white-space:pre">        </span> performing some operation that may potentially block, and for callbacks returning</div><div><span class="" style="white-space:pre">        </span> back to some blocking operation.  If this thread does not reclaim the VM before-</div><div><span class="" style="white-space:pre">        </span> hand then when the next heartbeat occurs the thread manager will schedule a</div><div><span class="" style="white-space:pre">        </span> thread to acquire the VM which may start running the VM in place of this thread.</div><div><br></div><div><span class="" style="white-space:pre">        </span> N.B. Most of the state needed to resume after preemption is set in preemptDisowningThread.&quot;</div><div><span class="" style="white-space:pre">        </span>&lt;api&gt;</div><div><span class="" style="white-space:pre">        </span>&lt;inline: false&gt;</div><div><span class="" style="white-space:pre">        </span>| vmThread result |</div><div><span class="" style="white-space:pre">        </span>&lt;var: #vmThread type: #&#39;CogVMThread *&#39;&gt;</div><div><span class="" style="white-space:pre">...</span></div><div><span class="" style="white-space:pre">        </span>(flags anyMask: DisownVMForProcessorRelinquish) ifTrue:<br></div><div><span class="" style="white-space:pre">                </span>[| proc |</div><div><span class="" style="white-space:pre">                </span> (proc := objectMemory splObj: foreignCallbackProcessSlot) ~= objectMemory nilObject ifTrue:</div><div><span class="" style="white-space:pre">                        </span>[foreignCallbackPriority := self quickFetchInteger: PriorityIndex ofObject: proc].</div><div><span class="" style="white-space:pre">                </span> relinquishing := true.</div><div><span class="" style="white-space:pre">                </span> self sqLowLevelMFence].</div><div><br></div><div>So how to install a process in the foreignCallbackProcessSlot?  See SmalltalkImage&gt;&gt;recreateSpecialObjectsArray.</div><div><br></div><div>SmalltalkImage&gt;&gt;recreateSpecialObjectsArray<br></div><div><span class="" style="white-space:pre">        </span>&quot;Smalltalk recreateSpecialObjectsArray&quot;</div><div><span class="" style="white-space:pre">        </span></div><div><span class="" style="white-space:pre">        </span>&quot;To external package developers:</div><div><span class="" style="white-space:pre">        </span>**** DO NOT OVERRIDE THIS METHOD.  *****</div><div><span class="" style="white-space:pre">        </span>If you are writing a plugin and need additional special object(s) for your own use, </div><div><span class="" style="white-space:pre">        </span>use addGCRoot() function and use own, separate special objects registry &quot;</div><div><span class="" style="white-space:pre">        </span></div><div><span class="" style="white-space:pre">        </span>&quot;The Special Objects Array is an array of objects used by the Squeak virtual machine.</div><div><span class="" style="white-space:pre">        </span> Its contents are critical and accesses to it by the VM are unchecked, so don&#39;t even</div><div><span class="" style="white-space:pre">        </span> think of playing here unless you know what you are doing.&quot;</div><div><span class="" style="white-space:pre">        </span>| newArray |</div><div><span class="" style="white-space:pre">        </span>newArray := Array new: 60.</div><div><span class="" style="white-space:pre">        </span>&quot;Nil false and true get used throughout the interpreter&quot;</div><div><span class="" style="white-space:pre">        </span>newArray at: 1 put: nil.</div><div><span class="" style="white-space:pre">        </span>newArray at: 2 put: false.</div><div><span class="" style="white-space:pre">        </span>newArray at: 3 put: true.</div><div><span style="white-space:pre">...</span></div><div><span class="" style="white-space:pre">        </span>&quot;Used to be WeakFinalizationList for WeakFinalizationList hasNewFinalization, obsoleted by ephemeron support.&quot;</div><div><span class="" style="white-space:pre">        </span>newArray at: 56 put: nil.</div><div><br></div><div><span class="" style="white-space:pre">        </span>&quot;reserved for foreign callback process&quot;</div><div><span class="" style="white-space:pre">&gt;&gt;&gt;        </span>newArray at: 57 put: (self specialObjectsArray at: 57 ifAbsent: []).</div><div><br></div><div><span class="" style="white-space:pre">        </span>newArray at: 58 put: #unusedBytecode.</div><div><span class="" style="white-space:pre">        </span>&quot;59 reserved for Sista counter tripped message&quot;</div><div><span class="" style="white-space:pre">        </span>newArray at: 59 put: #conditionalBranchCounterTrippedOn:.</div><div><span class="" style="white-space:pre">        </span>&quot;60 reserved for Sista class trap message&quot;</div><div><span class="" style="white-space:pre">        </span>newArray at: 60 put: #classTrapFor:.</div><div><br></div><div><span class="" style="white-space:pre">        </span>&quot;Now replace the interpreter&#39;s reference in one atomic operation&quot;</div><div><span class="" style="white-space:pre">        </span>self specialObjectsArray becomeForward: newArray</div><div><br></div><div><br></div><div>So chose a priority to run foreign callbacks at, write a pair of accessors to set and get the foreign callback process in the specialObjectsArray.  Set the slot to a new process ([] newProcess priority: N; yourself), save and restart the image (to allow the system to initialize correctly), and then see how you get on.  I expect you&#39;ll see crashes soon enough because...the MT VM is a prototype.  But you may just get lucky.  I certainly hope so!</div><div><br></div><div>Ah yes, you also have to arrange that when that process runs it replaces itself in the foreignCallbackSlot, so you&#39;ll have to work out a scheme to handle multiple foreign callbacks.  I wrote a scheme for VW, but haven&#39;t got this far with the Cog MT VM.</div><div><br></div><span style="font-size:small;border-collapse:separate"><div>_,,,^..^,,,_<br></div><div>best, Eliot</div></span></div></div></div>
</div></div>