Is it a **requirement** that FFI calls have a method all to themselves
like a primitive call?
I have this definition...
FFIExternalStructure subclass: #CXString
CXString class>>fieldsDesc
^ #(
void *data;
uint private_flags;
)
The instance returned by this...
Xxxx>>getClangVersion
^ self ffiCall: #( CXString clang_getClangVersion () ) module: Libclang
has private_flags=1 indicating the library allocated external memory
for *data and to release this I need to call clang_disposeString(
CXString string). However this doesn't guard against being called
twice and double-free()'ing CXString crashing the VM, so I defined the
following...
CXString>>dispose
self inform: 'debug_dispose1'.
self private_flags = 1
ifTrue:
[ self inform: 'debug_dispose2'.
self private_flags: 0.
self ffiCall: #( void clang_disposeString ( CXString
self ) ) module: Libclang
]
ifFalse:
[ self inform: 'debug_dispose3'.
Error signal: 'Cannot dispose twice' ].
However something strange, dispose will only execute once. For example for...
s := Libclang getClangVersion.
s dispose. "==>debug_dispose1, debug_dispose2"
s dispose. "==>nothing at all"
I would expect the second call to dispose
to result it "==>debug_dispose1, debug_dispose3"
but it does nothing at all. Debugging into "S dispose" displays the
source of dispose and then returns without executing anything. The
image seems fine and displays the updated zero in private_flags -- at
least for a few minutes, then it might crash indicating the
clang_disposeString() has been called twice.
However it works if I separate out the FFI call to a separate method...
CXString>>unsafeDispose
self ffiCall: #( void clang_disposeString ( CXString self ) )
module: Libclang
CXString>>dispose
self inform: 'dispose1'.
self private_flags = 1
ifTrue:
[ self inform: 'dispose2'.
self private_flags: 0.
self unsafeDispose.
]
ifFalse:
[ self inform: 'dispose3'.
Error signal: 'Cannot dispose twice' ].
s := Libclang getClangVersion.
s dispose. "==>debug_dispose1, debug_dispose2"
s dispose. "==>debug_dispose1, debug_dispose3"
s dispose. "==>debug_dispose1, debug_dispose3"
s dispose. "==>debug_dispose1, debug_dispose3"
Can someone hazard a guess what is behind this behaviour? I would
prefer not to require the unguarded #unsafeDispose.
cheers -ben