Hi igor but this is also important that if the user is a nice guy and the library is acting well we do not have to write a plugin for each callback, no? To me forcing people to write a plugin is not nice. Having solution that would work on normal use case is really nice to have.
Stef On Thu, Mar 16, 2017 at 12:04 PM, Igor Stasenko <siguc...@gmail.com> wrote: > > > On 16 March 2017 at 01:44, Eliot Miranda <eliot.mira...@gmail.com> wrote: >> >> >> Hi Igor, >> >> On Wed, Mar 15, 2017 at 2:53 AM, Igor Stasenko <siguc...@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.