How to access hs_free_fun_ptr from a Visual Studio DLL

Sven Panne Sven.Panne at aedion.de
Mon Mar 27 10:26:39 EST 2006


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.


More information about the Glasgow-haskell-users mailing list