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

Reply via email to