<div dir="ltr">Hi Craig,<div><br></div><div>    you need Alien-eem.24</div><div><br></div><div>Here&#39;s Alien class&gt;&gt;exampleCqsort</div><div><span class="" style="white-space:pre">        </span>&quot;Call the libc qsort function (which requires a callback).&quot;</div><div><span class="" style="white-space:pre">        </span>&quot;Alien exampleCqsort&quot;</div><div><span class="" style="white-space:pre">        </span>&quot;(Time millisecondsToRun: [100 timesRepeat: [Alien exampleCqsort]]) / 100.0&quot;</div><div><span class="" style="white-space:pre">        </span>| cb rand nElements sizeofDouble values orig sort |</div><div><span class="" style="white-space:pre">        </span>rand := Random new.</div><div><span class="" style="white-space:pre">        </span>values := Alien newC: (nElements := 100) * (sizeofDouble := 8).</div><div><span class="" style="white-space:pre">        </span>1 to: values dataSize by: sizeofDouble do:</div><div><span class="" style="white-space:pre">                </span>[:i| values doubleAt: i put: rand next].</div><div><span class="" style="white-space:pre">        </span>orig := (1 to: values dataSize by: sizeofDouble) collect: [:i| values doubleAt: i].</div><div><span class="" style="white-space:pre">        </span>cb := Callback</div><div><span class="" style="white-space:pre">                        </span>signature:  #(int (*)(const void *, const void *))</div><div><span class="" style="white-space:pre">                        </span>block: [ :arg1 :arg2 | ((arg1 doubleAt: 1) - (arg2 doubleAt: 1)) sign].</div><div><span class="" style="white-space:pre">        </span>(Alien lookup: &#39;qsort&#39; inLibrary: Alien libcName)</div><div><span class="" style="white-space:pre">                </span>primFFICallResult: nil</div><div><span class="" style="white-space:pre">                </span>with: values pointer</div><div><span class="" style="white-space:pre">                </span>with: nElements</div><div><span class="" style="white-space:pre">                </span>with: sizeofDouble</div><div><span class="" style="white-space:pre">                </span>with: cb thunk.</div><div><span class="" style="white-space:pre">        </span>sort := (1 to: values dataSize by: sizeofDouble) collect: [:i| values doubleAt: i].</div><div><span class="" style="white-space:pre">        </span>values free.</div><div><span class="" style="white-space:pre">        </span>^orig -&gt; sort</div><div><br></div><div>The above example uses Alien to make the callout.  To use it with the FFI simply pass cb pointer as the argument as is done above.</div><div><br></div><div><br></div><div>Implementation:</div><div><br></div><div>The way that it works is in two parts</div><div>- the VM passes up a pointer to a structure from which all arguments, stacked and in registers (because the VM has copied the register args into the struct) can be accessed, and through which the result can be returned.</div><div>- the image level provides marshalling methods that match the signature in the callback</div><div><br></div><div>So e.g. with a callback of<br></div><div><div><span class="" style="white-space:pre">                </span>Callback</div><div><span class="" style="white-space:pre">                        </span>signature:  #(int (*)(const void *, const void *))</div><div><span class="" style="white-space:pre">                        </span>block: [ :arg1 :arg2 | ((arg1 doubleAt: 1) - (arg2 doubleAt: 1)) sign]</div></div><div>the marshalling methods are in Callback&#39;s signature protocol:</div><div><br></div><div>Callback&gt;&gt;voidstarvoidstarRetint: callbackContext sp: spAlien</div><div><span class="" style="white-space:pre">        </span>&lt;signature: #(int (*)(const void *, const void *)) abi: &#39;IA32&#39;&gt;</div><div><span class="" style="white-space:pre">        </span>^callbackContext wordResult:</div><div><span class="" style="white-space:pre">                </span>(block</div><div><span class="" style="white-space:pre">                        </span>value: (Alien forPointer: (spAlien unsignedLongAt: 1))</div><div><span class="" style="white-space:pre">                        </span>value: (Alien forPointer: (spAlien unsignedLongAt: 5)))</div><div><br></div><div>where spAlien is an Alien pointing to a VMCallbackContext32.</div><div><br></div><div>For ARM support we would add</div><div><br></div><div>Callback&gt;&gt;voidstarvoidstarRetint: callbackContext sp: spAlien intRegArgs: regsAlien</div><div><span class="" style="white-space:pre">        </span>&lt;signature: #(int (*)(const void *, const void *)) abi: &#39;ARMV5&#39;&gt;</div><div><span class="" style="white-space:pre">        </span>^callbackContext wordResult:</div><div><span class="" style="white-space:pre">                </span>(block</div><div><span class=""><span class="" style="white-space:pre">                        </span>value: (Alien forPointer: (</span>regsAlien<span class=""> unsignedLongAt: 1))</span></div><div><span class=""><span class="" style="white-space:pre">                        </span>value: (Alien forPointer: (</span>regsAlien<span class=""> unsignedLongAt: 5)))</span></div><div><span class=""><br></span></div><div><span class="">Basically the idea is that the selector of the method doesn&#39;t matter except for the number of arguments.  What&#39;s important is the pragma which defines the signature and the ABI for which this is a valid marshalling method.</span></div><div><span class=""><br></span></div><div><span class="">When the callback is instantiated, Callback introspects to find the marshalling </span><span class="">method that matches the signature.  If one doesn&#39;t already exist you can write one.  Hopefully we&#39;ll write an ABI compiler that will automatically generate these marshalling </span>methods according to the platform&#39;s ABI, but for now its a manual process.  But at least it&#39;s open and flexible.  When the callback is invoked the evaluator is performed with the current callbackContext and pointer(s) to the arguments.  There is a 32-bit and a 64-bit callback context, and it can have a stack pointer, integer register args and floating point register args.  So it&#39;s general enough for any callback.</div><div><br></div><div>To pass back the result, a value is assigned into the struct via the accessor in the marshalling method and control returns to teh point where teh callback comes in, and this uses a primitive to return.  Inside the callbackCOntext is a jmpbuf from a setjmp.  The primitive longjmp&#39;s back to the entry point in the VM which extracts the result and the code for the kind of result and returns:</div><div><br></div><div>Callback class&gt;&gt;invokeCallbackContext: vmCallbackContextAddress &quot;&lt;Integer&gt;&quot; &quot;^&lt;FFICallbackReturnValue&gt;&quot;</div><div><span class="" style="white-space:pre">        </span>&quot;The low-level entry-point for callbacks sent from the VM/IA32ABI plugin.</div><div><span class="" style="white-space:pre">        </span> Return via primReturnFromContext:through:.  thisContext&#39;s sender is the</div><div><span class="" style="white-space:pre">        </span> call-out context.&quot;</div><div><span class="" style="white-space:pre">        </span>| callbackAlien type |</div><div><span class="" style="white-space:pre">        </span>callbackAlien := (Smalltalk wordSize = 4</div><div><span class="" style="white-space:pre">                                                </span>ifTrue: [VMCallbackContext32]</div><div><span class="" style="white-space:pre">                                                </span>ifFalse: [VMCallbackContext64])</div><div><span class="" style="white-space:pre">                                                        </span>atAddress: vmCallbackContextAddress.</div><div><span class="" style="white-space:pre">        </span>[type := Callback evaluateCallbackForContext: callbackAlien]</div><div><span class="" style="white-space:pre">                </span>ifCurtailed: [self error: &#39;attempt to non-local return across a callback&#39;].</div><div><span class="" style="white-space:pre">        </span>type ifNil:</div><div><span class="" style="white-space:pre">                </span>[type := 1. callbackAlien wordResult: -1].</div><div><span class="" style="white-space:pre">        </span>callbackAlien primReturnAs: type fromContext: thisContext</div></div><div class="gmail_extra"><br><div class="gmail_quote">On Thu, Sep 3, 2015 at 5:22 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:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><br>
Hi all--<br>
<br>
     I&#39;d like to use a C shared library via FFI from Squeak or Pharo.<br>
Specifically, I want to call a C function which takes as one of its<br>
parameters a pointer to an array of callback function addresses. Does<br>
one of the FFI approaches floating around handle that, and provide a<br>
relatively pleasant mapping between Smalltalk block closures and C<br>
callback function addresses?<br>
<br>
     I was doing this so far in GemStone, but apparently its FFI only<br>
supports passing callback function addresses one at a time as direct<br>
parameters of C callouts. I suppose I could write a wrapper C library<br>
around the one I really want to use, that provides the callback setup<br>
interface that GemStone expects, but whenever I have to write actual C<br>
code I start to think &quot;Hm, why don&#39;t I just use a Smalltalk VM for which<br>
I have the source?&quot; :)  All the fancy distributed object-database stuff<br>
that my project also wants can wait for a bit.<br>
<br>
<br>
     thanks!<br>
<br>
-C<br>
<br>
--<br>
Craig Latta<br>
<a href="http://netjam.org" rel="noreferrer" target="_blank">netjam.org</a><br>
<a href="tel:%2B31%20%20%206%202757%207177" value="+31627577177">+31   6 2757 7177</a> (SMS ok)<br>
<a href="tel:%2B%201%20415%20%20287%203547" value="+14152873547">+ 1 415  287 3547</a> (no SMS)<br>
<br>
<br>
</blockquote></div><br><br clear="all"><div><br></div>-- <br><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>