On Mon, Sep 12, 2016 at 6:08 PM, Henrik Johansen < henrik.s.johan...@veloxit.no> wrote:
> > On 07 Sep 2016, at 5:38 , Ben Coman <b...@openinworld.com > <b...@openinworld.com>> wrote: > > On Wed, Sep 7, 2016 at 10:42 PM, Ben Coman <b...@openinworld.com> wrote: > > On Wed, Sep 7, 2016 at 9:09 PM, Esteban Lorenzano <esteba...@gmail.com> > wrote: > > > On 07 Sep 2016, at 14:56, Ben Coman <b...@openinworld.com > <b...@openinworld.com>> wrote: > > On Tue, Sep 6, 2016 at 8:08 PM, Esteban Lorenzano <esteba...@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