ForeignPtr's - why can't they be passed directly to foreign functions?

Esa Ilari Vuokko eivuokko at gmail.com
Wed Mar 15 07:27:26 EST 2006


On 3/15/06, Brian Hulley <brianh at metamilk.com> wrote:
> 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


More information about the Glasgow-haskell-users mailing list