module NHC.FFI
( ForeignObj -- abstract, instance of: Eq
, makeForeignObj -- :: Addr -> IO () -> IO ForeignObj
, newForeignObj -- :: Addr -> IO () -> IO ForeignObj
, freeForeignObj -- :: ForeignObj -> IO ()
, foreignObjToAddr -- :: ForeignObj -> Addr
, withForeignObj -- :: ForeignObj -> (Addr -> IO a) -> IO a
, touchForeignObj -- :: ForeignObj -> IO ()
) where
import Addr (Addr)
import NHC.Internal (unsafePerformIO, _mkIOok1)
data ForeignObj; -- primitive type known to compiler internals
-- Note that the type of makeForeignObj expects the finalizer to already
-- have been applied to the Addr, and here we also need to wrap it inside
-- an `unsafePerformIO'. Furthermore, we then need to wrap that inside
-- a box so that the primitive call does not evaluate it on the way in!
data _E a = _E a -- just a box to protect arg from evaluation
-- The only way to create a ForeignObj.
makeForeignObj :: Addr -> IO () -> IO ForeignObj
makeForeignObj a f = primForeignObjC a (_E (unsafePerformIO f))
-- A different name for the same thing.
newForeignObj :: Addr -> IO () -> IO ForeignObj
newForeignObj = makeForeignObj
-- Note that `primForeignObjC' does not strictly conform to the FFI
-- standard. It is not legal to return a ForeignObj as the result of
-- a foreign import. To return a ForeignObj from C, you have to first
-- get it as an Addr, then attach the finaliser using `makeForeignObj'.
-- However, in order to implement the latter, we need one single instance
-- of returning a ForeignObj, and this is it. ***Do not do it elsewhere!
foreign import ccall primForeignObjC :: Addr -> a -> IO ForeignObj
-- Get the hidden Addr out of a ForeignObj.
foreign import cast foreignObjToAddr :: ForeignObj -> Addr
-- But it is impossible to do the opposite!
--foreign cast addrToForeignObj :: Addr -> ForeignObj -- WRONG!
-- Just occasionally, we really want to finalise a ForeignObj early.
-- This is slightly dangerous, because the ForeignObj could remain live
-- indefinitely following its finalisation, allowing nasty people to
-- continue using it (and seg-faulting as a result!)
freeForeignObj :: ForeignObj -> IO ()
freeForeignObj fo = _mkIOok1 reallyFreeForeignObj fo
-- The true freeing operation must be implemented outside the FFI, because
-- a ForeignObj passed via the FFI is just the Addr it contains, not the
-- whole value including the finaliser. Note that this operation calls the
-- finaliser for the Addr, then additionally releases the ForeignObj storage
-- itself for possible re-use.
reallyFreeForeignObj primitive 1 :: ForeignObj -> ()
-- New operation suggested by Marcin Kowalcsycz.
-- It is a safer way to use the older, less safe, `foreignObjToAddr'.
withForeignObj :: ForeignObj -> (Addr -> IO a) -> IO a
withForeignObj fo action = action (foreignObjToAddr fo)
{- Note that GHC probably requires the following implementation:
do
res <- action (foreignObjToAddr fo)
touch fo
return res
-}
-- `Touching' a foreignObj is just intended to keep it alive across
-- calls which might otherwise allow it to be GC'ed. Only really
-- an issue in GHC - for nhc98 a null-op is sufficient.
touchForeignObj :: ForeignObj -> IO ()
touchForeignObj fo = return ()
|