Hi Igor,<br><br><div class="gmail_quote">On Mon, May 3, 2010 at 4:31 PM, Igor Stasenko <span dir="ltr">&lt;<a href="mailto:siguctua@gmail.com">siguctua@gmail.com</a>&gt;</span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex;">
Hello,<br>
Andreas and Eliot,<br>
<br>
i direct my question mainly to you , because you are most experienced<br>
in this area, but if there&#39;s others who want to share their thoughts,<br>
please do so.<br>
<br>
So, the question is, what to do, if an error triggered during a callback.<br>
My concern is, that sometimes its unsafe to do return from callback<br>
with invalid value.<br></blockquote><div><br></div><div>At one (low) level it depends on what kind of code you&#39;re calling.  Essentially the problem is how to cut-back the C call stack.  Since the FFI machinery knows the extent of the C stack between the FFI callout and the callback it is straight-forward to provide an escape primitive that cuts back the C stack to the point of the FFI callout within which the callback occurred.  But one can&#39;t simply cut-back the stack if one is calling code that depends on unwind-protect (e.g. C++).  If you want to support C++ the FFI callout could use a try-catch around the actual callout (something you&#39;d want to do anyway if you wanted the FFI to be able to catch exceptions and answer them as primitive failures).  To cut-back the stack the primitive  set a flag and throw a special exception that would be caught by the try-catch in the FFI callout.  The catch block would test for the flag and do a special return, e.g. return a failure value or fail the FFI callout primitive.  I like the primitive failure response because the response to FFI failures could convert the failure into a Smalltalk exception that could be engineered to continue propagating the exception that caused you to want to unwind the callback.</div>
<div><br></div><div>Another approach is to say that this is a fatal error in a deployed application (because, as you can see from the above, you need some complex machinery to deal with the situation generally) and that during development all one is able to do is inform the programmer of what has happened so they can try and avoid it.</div>
<div><br></div><div>Another approach (which IIRC is the one VisualWorks takes) is just to return some default error value to the callback.  The VW machinery puts a generic exception handler round the invocation of Smalltalk code from the callback trampoline so that it catches any uncaught exception raised within the Smalltalk side of the callback.  The exception handler arranges to answer a default value to the callback and consume the exception.</div>
<div><br></div><div>I think you&#39;ll be able to find some papers on propagating exceptions across language boundaries (which is what this is really about) and see what high-quality FFIs do here.  Finding the cost/benefit justification for implementing it is another thing altogether :)  It seems to me that a reasonable approach is as follows<b>:</b></div>
<div><br></div><div>1.  no attempt is made to propagate Smalltalk exceptions to foreign code or vice verse.  e.g. if an exception occurs in Smalltalk code invoked in the context of a callback then it can propagate freely beyond the FFI callout within which the callback occurred.</div>
<div><br></div><div>2. given that exceptions are not propagated what we require is correct propagation of unwinds (invocation of finally blocks in C++) to foreign code if and when the Smalltalk stack is unwound around an FFI callout/callback invocation.</div>
<div><br></div><div>To implement this one needs two things</div><div>- to put an unwind-protect around the invocation of the Smalltalk code from a callback</div><div>- to provide a primitive that will unwind the C stack back from a callback to its corresponding FFI callout /and not/ return from the FFI callout, instead somehow returning from the primitive invocation.</div>
<div><br></div><div>The last thing isn&#39;t that hard to do because we have contexts.  The unwind-a-callback primitive can save the calling context and the try-catch in the FFI callout can arrange to return to it, not to the sender of the FFI invocation.</div>
<div><br></div><div>So when the Smalltalk stack is unwound as the last part of handling the exception within the callback we cause any finally blocks in C++ to be run as the C stack is unwound from our special callback-unwind primitive.  A stack containing multiple callout/callback invocations can be correctly unwound over multiple callout/callback invocations.  If within a callout/callback invocation there are callouts and callbacks to other language systems invisible to us then providing these also support correct unwinding we won&#39;t break anything because we&#39;re unwinding correctly.</div>
<div><br></div><div>Does this make any sense?</div><div><br></div><div>best</div><div>Eliot</div><div><br></div><div><br></div><div>HTH</div><div>Eliot</div><div><br></div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex;">

<br>
For example, if callback&#39;s caller expects a pointer to something,<br>
returning a default value (NULL)<br>
will have the same effect as simply terminating VM :)<br>
<br>
Here&#39;s my snippet of code, which i coded so far:<br>
<br>
block: aBlock<br>
<br>
        self numberOfArguments = aBlock numArgs ifFalse: [<br>
                self error: &#39;Invalid number of arguments&#39; ].<br>
<br>
        context := [    &quot;here we entering a callback&quot;<br>
            [<br>
                [ | args |  args := self readArguments.<br>
                  self leave: (aBlock valueWithArguments: args) ]<br>
                        ifCurtailed: [ self error: &#39;attempt to non-local return across a callback&#39; ]<br>
<br>
            ] repeat.<br>
        ] asContext<br>
<br>
<br>
when callback is activated, an interpreter should enter the context ,<br>
stored in callback&#39;s context ivar.<br>
<br>
The best what i invented so far, is to wrap the whole thing in repeat block.<br>
So, if there&#39;s error happens, there could be at least chance that user<br>
(like me ;) might supply the right argument to the #leave:  method,<br>
using debugger.<br>
A leave: method serves to leave callback , but also might fail, if it<br>
can&#39;t coerce a return value to corresponding C equivalent.<br>
<br>
Of course, there&#39;s many other variants, like<br>
self leave: ( [ aBlock valueWithArguments: args ] ifCurtailed: [ self<br>
askUserToEnterTheValue ] )<br>
<br>
i&#39;d like to know, what you think would be the best variant.<br>
<font color="#888888"><br>
<br>
--<br>
Best regards,<br>
Igor Stasenko AKA sig.<br>
</font></blockquote></div><br>