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).
In you case, you will have something like:
FFIExternalObject subclass: #CXString
Thanks Esteban for the detailed description. However I've come to the conclusion that the C-library dynamically allocating memory for a field inside the struct of CXString doesn't exactly fit any of the pre-existing FFI types. When CXString subclasses FFIExternalObject the call to clang_getClangVersion () crashes the VM since its handle ==> ExternalAddress of 4 bytes but 8 bytes are required for a pointer + uint, https://github.com/llvm-mirror/clang/blob/google/stable/include/clang-c/CXSt... /* The CXString type is used to return strings from the interface when * the ownership of that string might differ from one call to the next. * Use clang_getCString() to retrieve the string data and, once finished * with the string data, call clang_disposeString() to free the string. */ typedef struct { const void *data; unsigned private_flags; } CXString; In my limited experience so far it seems FFIExternalStructure is most appropriate So what I've got working is... FFIExternalStructure subclass: #CXString instanceVariableNames: '' classVariableNames: '' poolDictionaries: 'CXStringFlag' package: 'Libclang' CXString class>>fieldsDesc ^ #( void *data; uint private_flags; ) Thus handle of CXString new ==> ByteArray of 8 bytes. However ByteArray and FFIExternalStructure doesn't have any of the smarts of ExternalAddress and FFIExternalReference. So... CXString>>autoRelease self class finalizationRegistry add: self CXString>>finalize self dispose. Transcript crShow: 'debug CXString disposed'. CXString>>dispose self private_flags = CXS_Malloc value "as set by C library" ifTrue: [ self private_flags: CXS_Disposed value. self unguardedDispose. ] ifFalse: [ Error signal: 'Guard prevents double-dispose' ]. CXString>>unguardedDispose self ffiCall: #( void clang_disposeString ( CXString self ) ) module: Libclang
CXString class>>#finalizeResourceData: version self ffiCall: #(void clang_disposeString(void *version))
This only seems applicable to FFIExternalReference subclasses. I guess I could copy FFIExternalReference>>autoRelease to CXString. Do you think there would be much advantage to doing this? I see FFIExternalResourceExecutor>>finalize has some session tracking, presumably to avoid freeing dangling references. but I'm not sure about the semantics of #addResource:'s call to #resourceData, and what CXString>>resourceData would return. cheers -ben