
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.