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