On Mon, Sep 12, 2016 at 6:08 PM, Henrik Johansen < henrik.s.johansen@veloxit.no> wrote:
On 07 Sep 2016, at 5:38 , Ben Coman <btc@openInWorld.com <btc@openinworld.com>> wrote:
On Wed, Sep 7, 2016 at 10:42 PM, Ben Coman <btc@openinworld.com> wrote:
On Wed, Sep 7, 2016 at 9:09 PM, Esteban Lorenzano <estebanlm@gmail.com> wrote:
On 07 Sep 2016, at 14:56, Ben Coman <btc@openInWorld.com <btc@openinworld.com>> wrote:
On Tue, Sep 6, 2016 at 8:08 PM, Esteban Lorenzano <estebanlm@gmail.com> wrote:
Hi,
sorry for arriving so late to this, but I was on holidays :) this is how autoRelease works:
1) #autoRelease of an object registers object for finalisation with a particular executor. Then behaviour is divided:
2.1.1) for ExternalAddresses, it just registers in regular way, who will call #finalize on GC 2.1.2) finalize will just call a free assuming ExternalAddress was allocated (which is a malloc)
2.2.1) for all FFIExternalReference, it will register for finalisation what #resourceData answers (normally, the handle of the object) 2.2.2) finalisation process will call the object class>>#finalizeResourceData: method, with the #resourceData result as parameter 2.2.3) each kind of external reference can decide how to free that data (by default is also just freeing).
An example of this is how CairoFontFace works (or AthensCairoSurface).
At the bottom of FFIExternalResourceExecutor class comment I read... "Note that in #finalizeResourceData: you cannot access any other properties of your instance, since it is already garbage collected."
But in my experiments it seems okay to access instance variables in #finalize. For example...
CXString >> autoRelease self class finalizationRegistry add: self
CXString >> finalize Transcript crShow: 'Finalizing CXString ' ; show: self private_flags. self dispose. Transcript show: ', done!'.
CXString >>private_flags "This method was automatically generated" ^handle unsignedLongAt: 5
Libclang getClangVersion autoRelease. Smalltalk garbageCollect. "==> Finalizing CXString 1, done! "
Is this an unlucky coincidence? Or maybe something changed from NB to UFFI? (There is a reference to NB there)
yes, is a coincidence. the idea of using #finalizeResourceData: is that you keep minimal information (in general, just the handle)⦠this way we ensure instances will be collected because we will not have circular references (preventing the weakregistry to work).
In general, you can always implement as you did it, but I would prefer the #finalizeResourceData: approach, even for structures. The only reason it is not implemented is because I didnât reach the necessity, then I just skipped it (not in purpose, it was not in my head :P), but now is a good moment to implement it⦠if you want it :)
Thanks for the offer, but hold off for the moment. I think I actually need more than just finalization session management. To get a real displayable string requires calling the clang_getCString() library function. I imagine I'd like to call this from CXString>>printOn: -- but this of course this would break after restarting the image.
extern "C" { const char *clang_getCString(CXString string) { .... return static_cast<const char *>(string.data); }}
typedef struct { const void *data; unsigned private_flags; } CXString;
So I think I need to register CXString with SessionManager to set *data <-- 0 on startup.
An alternative might be adding a session variable to CXString, FFIExternalStructure subclass: #CXString instanceVariableNames: 'session' classVariableNames: '' poolDictionaries: 'CXStringFlag' package: 'Libclang'
except doing so crashes when calling.... getClangVersion ^ self ffiCall: #( CXString clang_getClangVersion () ) module: Libclang
So this worked...
CXString >> resetData handle unsignedLongAt: 1 put: 0.
CXString class >> startUp: resuming resuming ifTrue: [ self allInstances do: [ :cxs | cxs resetData ] ].
CXString class >> initialize "self initialize" SessionManager default registerSystemClassNamed: self name
Now immediate after a save/restart, doing... CXString allInstances first getString "==> UndefinedObject(nil)" instead of crashing the VM.
Can you see/guess any traps hidden from me? Now I wonder early it is practical to prioritise such a reset. My first thought is at least before #printString starts getting called ??
For traps, there's the assumption in resetData that pointer stored in handle is unsignedLong-sized. Shouldn't there be auto-generated accessors for #data you can use instead to change the value to Pointer void?
Cheers, Henry
Good point. This is the auto-generated accessor... data: anObject handle pointerAt: 1 put: anObject getHandle. However getHandle is only understood by ExternalObject and FFIExternalArray, and it seems a bit awkward to create a null on of those just to zero the handle. Actually your comment lead me to dicover ExternalAddress>>beNull, which looked promising. However the /handle/ of a FFIExternalStructure is a ByteArray rather than an ExternalAddress (which I guess makes sense). So... 1. maybe push beNull up to ByteArray, and maybe also isNull 2. #beNull implementation is 'self atAllPut: 0' which is understood by ByteArray. In my case I don't care if private_flags is also cleared, so maybe... CXString >> beNull handle atAllPut: 0. cheers -ben