
On 3/15/06, Brian Hulley
Hi - I've got the beginnings of an API for a GUI system as follows:
data DWindow a data DEdit a
type Window = DWindow type Edit a = DWindow (DEdit a)
foreign import ccall duma_init :: IO () foreign import ccall duma_run :: IO ()
foreign import ccall duma_release :: FunPtr (Ptr (Window a) -> IO ())
foreign import ccall duma_createEdit :: IO (Ptr (Edit a)) foreign import ccall duma_addTop :: Ptr (Window a) -> IO ()
createEdit :: IO (ForeignPtr (Edit a)) createEdit = do edit <- duma_createEdit newForeignPtr duma_release edit
(Not directly related, but maybe useful to know) Stricly speaking, asynchronous exception may occur in between, and this code should in fact be "surrounded" by block to prevent resource leaks. createEdit = block $ do edit <- duma_createEdit newForeignPtr duma_release edit
addTop :: ForeignPtr (Window a) -> IO () addTop w = withForeignPtr w duma_addTop
This works, but it seems a bit of a pain to have to manually convert between ForeignPtr's and Ptr's all the time. In particular, for the definition of addTop, I tried:
foreign import ccall "duma_addTop" addTop :: ForeignPtr (Window a) -> IO ()
but got an error because ForeignPtr's are not allowed as part of the type of a foreign function. Since the definition of ForeignPtr is just void *, I wonder why this restriction exists - ie is a ForeignPtr not just the same address as the corresponding Ptr?
First, Ptr and ForeignPtr are totally diffrent beasts. Ptr is just plain address, while ForeignPtr might have associated finalisers. When Ptr is garbage collected, there is nothing diffrent from collecting other simple types. Just throw the (as an implementation detail) the integer holding address away. When ForeignPtr is garbage collected, the finaliser must be run (or scheduled to run). which requires extra bookkeeping and quite a bit of work on part of runtime. In short: it could be handled as you like. (I think - just need ForeignPtr to C pointer conversion and guarantee ForeignPtr won't die while in ffi call) Reality: There is no magic in ForeignPtr for ffi calls, and hence it's just like any other haskell object (like any other boxed value, anyway) - the parameter given to the function might be last reference to the value, and it might be optimised/thrown away just before the actual function call, get garbage collected and resource might be free'd. Which certainly isn't what you want.
My other question is what happens if I want to have a function that takes more than one ForeignPtr as argument ie
foreign import ccall duma_test :: Ptr (Window a) -> Ptr (Window a) -> IO ()
test :: ForeignPtr (Window a) -> ForeignPtr (Window a) -> IO () test p q = withForeignPtr p (\p' -> withForeignPtr q $ duma_test p')
Is this the only way to achieve this? It seems a bit long-winded and possibly a bit inefficient...
I would like to know answer to this question as well. I quite often would like to have an framework to handle (ffi) resource in more convient manner. Typically, I write few simple combinators for with-style functions. If not, using $ might make code nicer than (). withForeignPtr foo $ \foo -> withForeignPtr bar $ \bar -> do use foo use bar return baz I haven't tried if there is a big performance loss from multiple nested block and unblock calls those with-functions require. If there is, a framework could actually be optimised.
One other question: if I use forkIO within Haskell, am I right in thinking that the lightweight concurrent threads are safe to use with my single threaded C code ie that there is no danger of a thread being preemptively halted while it is inside a foreign function?
If I understand correctly, GHC RTS won't halt the thread while it's performing ffi call. HTH, -Esa