How to access hs_free_fun_ptr from a Visual Studio DLL
Brian Hulley
brianh at metamilk.com
Mon Mar 27 07:27:04 EST 2006
Sven Panne wrote:
> Am Samstag, 25. März 2006 20:00 schrieb Brian Hulley:
>> I've found a workaround to the problem below: instead of trying to
>> use hs_free_fun_ptr, I instead pass a FunPtr to the Haskell function
>> freeHaskellFunPtr into my DLL, and use this to free everything,
>> finally using it to free itself (!) which I assume should be safe.
>> [...]
>
> It has been quite some time since I've worked on GHC's Adjustor.c and
> Hugs'
> FFI, but IIRC it is in general *not* safe to do this. On some
> platforms code
> is being generated dynamically for these kind of callbacks, which has
> already
> been freed by the time the callback returns. This might appear to
> work,
> depending on your processor architecture and dynamic memory management
> behaviour, but it's better not to rely on this. Perhaps the FFI spec
> should
> be clearer here.
I've been thinking about this a bit more, and I'm now puzzled at why there
should be a problem, even if some dynamic code is generated. In Section
5.4.2 of the Haskell FFI 1.0 Addendum 98, freeHaskellFunPtr is specified as:
freeHaskellFunPtr :: FunPtr a -> IO ()
Release the storage associated with the given FunPtr, which must have
been obtained
from a wrapper stub. This should be called whenever the return value
from
a foreign import wrapper function is no longer required; otherwise, the
storage it uses will leak.
Thus all the function is supposed to do is just release the storage
associated with the FunPtr itself. It is not supposed to affect the storage
of the code for the function pointed to by the FunPtr, other than to inform
the garbage collector that there is now one less reference to it.
I'd have thought that even if the function code is generated dynamically,
the FunPtr would just be treated as an extra root (leading to the block of
memory storing the code) as far as garbage collection is concerned.
freeHaskellFunPtr would then remove this extra root. However if the code is
currently being executed, surely the (machine code) program counter would
act as a root for gc, to prevent the block containing the currently
executing code (and any blocks on the call stack) from being reclaimed?
Otherwise, freeHaskellFunPtr is not safe to use anywhere (perhaps that's why
you used a scavenger function?). For example, in:
foreign import ccall "wrapper" mkIO :: IO () -> IO (FunPtr (IO ()))
foreign import ccall set_callback :: FunPtr (IO ()) -> IO ()
foreign import ccall run :: IO ()
foo1 :: IO ()
foo1 = do
set_callback foo2
something_else -- does this code still exist?
foo2 :: IO ()
foo2 = ...
main = do
set_callback foo1
run -- causes foo1 to be called at some point
where set_callback wraps its arg in a FunPtr and calls freeHaskellFunPtr if
a previous callback function has already been specified.
If foo1 is generated as dynamic code, then freeing the FunPtr to it as a
consequence of changing the callback to foo2, becomes problematic unless
there is a guarantee that currently executing code is always considered to
be "rooted" by the program counter.
So my questions are:
1) Is my understanding of the FFI definition of freeHaskellFunPtr incorrect?
2) Are some implementations not treating the program counter and call stack
as roots for blocks of memory which contain dynamically generated code that
is currently executing?
3) Are some implementations treating a FunPtr as the sole owner of the code
it is pointing to, thus requiring the workaround of a scavenger function to
safely free FunPtr's at a point where it is known that none of the code
pointed to by any of the FunPtr's is currently executing?
Thanks, Brian.
More information about the Glasgow-haskell-users
mailing list