
Am Montag, 27. März 2006 14:27 schrieb Brian Hulley:
[...] 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?
Partially, yes. Your example above leaves out an important point: Calling mkIO. Regardless of the implementation details, this has to accomplish two things: * Making sure that the Haskell function being wrapped is not garbage collected. This is important, because the returned FunPtr might contain the last reference to the wrapped function internally. * Setup some mechanism which does the "impedance matching" between C and Haskell, i.e. put the arguments in the right places, do some stack fiddling, etc. For GHC (and Hugs, IIRC), this is solved by generating a StablePtr to the Haskell function and "baking" it somehow into dynamically generated code (see ghc/rts/Adjustor.c), which does the argument/stack fiddling and then jumps to some C stub, which in turn uses the StablePtr and the rest of the arguments to call the Haskell function. If this sounds tricky and highly platform-dependent: Yes, it is! :-) The main point here is: The Haskell runtime doesn't know the time when it is safe to free the StablePtr and the dynamically generated adjustor code. The FunPtr's C equivalent could be stored anywhere in C land, perhaps in some GUI callback table, some global variables, it could be in use by some other (non-Haskell) thread, etc. etc. Consequently, the programmer must help via freeHaskellFunPtr/hs_free_fun_ptr, but this should not happen while the FunPtr is in use, e.g. while the Haskell callback is currently being executed. The technical reason for this is that after returning from Haskell land, the adjustor code might need to do some cleanup: C -> adjustor -> stub -> Haskell -> stub -> adjustor -> C It could be the case that the adjustor tail-jumps to the stub, but this is not guaranteed to be the case for all platforms.
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?
As hopefully explained above, a pointer to the adjustor could be everywhere, so GC is not an option here.
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?
Ownership is not the problem here, but lifetime: A callback should simply not free its own FunPtr. This requirement is not much different from having to avoid accessing memory which has already been freed. I know that this is all complicated stuff, but if you would really like to understand the mechanisms behind, it would probably be best to pick a platform you know and have a look at dsFExportDynamic in ghc/compiler/deSugar/DsForeign.lhs and ghc/rts/Adjustor.c. Cheers, S.