[Vm-dev] BUG? A problem with callbacks that shows up in 64bits (but is on 32bits too)

Igor Stasenko siguctua at gmail.com
Thu Mar 16 11:04:37 UTC 2017


On 16 March 2017 at 01:44, Eliot Miranda <eliot.miranda at gmail.com> wrote:

>
> Hi Igor,
>
> On Wed, Mar 15, 2017 at 2:53 AM, Igor Stasenko <siguctua at gmail.com> wrote:
>
>>
>> Here's my d)
>> implement callback functions in C, or in native form => no need for
>> entering the smalltalk execution => no risk of GC => nothing to worry about.
>>
>> I guess nobody will like it (and will be right, of course ;) , but it is
>> how it was originally done. I used NativeBoost to implement those callback
>> functions and they're won't cause any GC problems.
>>
>
> yes, I like this.  I was wondering why the callbacks solution was used at
> all yesterday.  All they do is redirect to the cairo library.  What are the
> reasons?  Tedious to write and maintain the necessary simple plugin?
>
> Clément pointed out a really ugly problem with the current
> implementation.  If one calls back into Pharo from the BitBlt primitives
> and then reinvokes BitBlt, say by innocently putting a halt in those
> callbacks, then the original BitBlt's state will get overwritten by the
> BitBlt invocations in the callback's dynamic exert.  At least with my
> changes the BitBlt primitive will abort, rather than continue with the
> invalid state.
>
>
>> That, of course, gave me solution in this concrete case, but not in
>> general.. i.e. : if you have another callback that cannot be implemented
>> na(t)ively, then
>> you facing similar problems, mainly: how to work around the problem, that
>> primitive(s) that using callbacks may capture state, that are subject of GC
>> activity.
>>
>> In general , then, i think such primitive should be (re)written in such
>> way , that it won't get puzzled by GC.. and addGCRoot(s), IMO then best
>> way, from general interfacing/implementation standpoint.
>> I would just add extra interface for using it especially in primitives,
>> so that it
>> 1) won't punish primitive writer with too much coding
>> 2) automatically handle primitive/callback nesting e.g.
>> primitive1 -> adds roots1 -> calls fn -> callback -> st code ->
>> primitive2 -> adds roots2 -> calls fn2 -> callback2 ...
>>
>>
>> something like this:
>>
>> static initialized once myprimooptable = [ a,b,c].
>> vm pushPrimRoots: myooptable.
>> self do things primitive does.
>> vm popPrimRoots
>>
>> or, since we have green threading, then maybe better will be in this
>> form:
>>
>> rootsId := static initialized once myprimooptable = [ a,b,c].
>> vm pushPrimRoots: myooptable.
>> self do things primitive does.
>> vm popPrimRoots: rootsId.
>>
>
> We kind of have this with the addGCRoot: interface.  But I think it's much
> better to design the system so that the primitive fails and can be
> retried.  The problem there is having to have the primitive failure code
> check and roll back.  For example in the copyBits primitive one sees
>
> ((sourceForm isForm) and: [sourceForm unhibernate])
> ifTrue: [^ self copyBits].
> ((destForm isForm) and: [destForm unhibernate])
> ifTrue: [^ self copyBits].
> ((halftoneForm isForm) and: [halftoneForm unhibernate])
> ifTrue: [^ self copyBits].
>
> This is really scruffy because...GrafPort implements copyBits, so this
> ends up not just retrying the primitive but running a lot more besides.
> One way to write it is
>
>
> ((sourceForm isForm) and: [sourceForm unhibernate])
> ifTrue: [^ self perform: #copyBits withArguments: #() inSuperclass:
> BitBlt].
> ((destForm isForm) and: [destForm unhibernate])
> ifTrue: [^ self perform: #copyBits withArguments: #() inSuperclass:
> thisContext method methodClass].
> ((halftoneForm isForm) and: [halftoneForm unhibernate])
> ifTrue: [^ self perform: #copyBits withArguments: #() inSuperclass:
> thisContext methodClass].
>
> but that's ugly.
>
> A mechanism that was in the VM would be nice.  The state for the
> invocation is saved on the stack.  So there could be a special failure path
> for this kind of recursive invocation problem; another send-back such as
> doesNotUnderstand: attemptToReturn:through:.  Note (I'm sure you know this
> Igor)  that there are primitives such as the ThreadedFFIPlugin's call-out
> primitive that very much expect to be invoked recursively and have no
> problem with it.
>
>
Yes, i know it. But keep in mind, that reentrant FFI callout mechanism
means *just* reentrant FFI callout code, it doesn't means that things, it
will be calling, automagically become reentrant as well.
And the above note about "Clément pointed out a really ugly problem" is
good example of it :)
And since you cannot predict/prevent/pretend/protect every single piece of
code, that FFI users are going to call, there's no solution to that, unless
users understand what they do and be aware of pitfalls and consequences.

It is quite easy to say "meh.. your FFI don't works.. go away, come year
later.. i will use other (put other language/software system there)"..
mostly because of ignorance, that there's a limits on what FFI can do and
what can't.

-- 
Best regards,
Igor Stasenko.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.squeakfoundation.org/pipermail/vm-dev/attachments/20170316/e19a3c42/attachment.html>


More information about the Vm-dev mailing list