��Hi Igor,On Wed, Mar 15, 2017 at 2:53 AM, Igor Stasenko <siguctua@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.