[Haskell-cafe] question re: FFI and ensuring unwrapped values are present on stack

adam vogt vogt.adam at gmail.com
Mon Mar 3 23:17:32 UTC 2014


Hello Charles,

I don't know what can be done for your option 1, but I have two
suggestions for your option 2:

Did you consider making RValue a ForeignPtr and adding a FunPtr to
rb_gc_unregister as the finalizer? That will end up being a bit less
restrictive than your "withObject", in exchange for a weaker guarantee
as to when the finalizer actually runs.

Also you might use the same trick that Control.Monad.ST does to
prevent references from leaking out. In other words, change your
definitions to look more like:

newtype RValue s = RValue (ForeignPtr ())

withObject :: (forall s. RValue s -> IO r)
     -> IO r

With that trick, (withObject return) becomes a type error.

Regards,
Adam


On Mon, Mar 3, 2014 at 12:41 AM, Charles Strahan
<charles.c.strahan at gmail.com> wrote:
> Hello all,
>
> This is probably going to be a pretty niche question, but I'm hoping someone
> here can lend a hand.
>
> I'm working on a binding for the YARV Ruby VM, and I'm struggling to come up
> a with a good interop story with respect to its GC implementation. I have
> two options to prevent premature GC of Ruby object pointers:
>
> 1) Guarantee that the pointers reside on the stack or in registers,
> 2) or copy the pointer itself to another static area of memory and register
> that address with the GC.
>
> So, the big question is: is there a way to make #1 a reality, while
> operating in the IO monad?
>
> I can picture something like the following:
>
> newtype RValue = RValue (Ptr RValue) deriving (Storable)
>
> withObject :: IO RValue         -- e.g. result of some ccall, or some
> further transformation thereof
>            -> (RValue -> IO a)  -- the RValue is now on the stack (or in a
> register), available for use without worry of premature GC
>            -> IO a              -- the result of computation
>
> With Haskell being a non-strict language, I understand that there are a lot
> of instances where a value could end up on the heap, with a thunk being
> passed via stack/registers. For my needs, I'd need some way to guarantee
> that the RValues being passed to and from withObject are at all times kept
> unwrapped and off the heap, and likewise within the second argument.
>
> Given my rather limited knowledge of Haskell (I've been programming in
> Haskell for about a month now), I think the best I can come up with is the
> second option suggested above:
>
> withObject :: (Ptr RValue -> IO ()) -- e.g. void some_fun(VALUE* out) {...}
>            -> (RValue -> IO a)      -- e.g. do something with `out` derefed
>            -> IO a
> withObject f g =
>     alloca $ \ptr -> do
>         rb_gc_register_address ptr   -- prevent GC
>         f ptr                        -- call wrapped native function
>         val <- peek ptr              -- deref the VALUE*
>         res <- g val                 -- pass to function
>         rb_gc_unregister_address ptr -- clean up
>         res
>
> So, while the above would work, it's really, really ugly (IMO). That, and
> I'd have to wrap all the native functions so that instead of returning
> VALUE, they'd write to some out ptr and return void. And there's the
> otherwise unnecessary alloca.
>
> I hope that was clear enough - any advice would be super duper appreciated!
>
> I'm assuming that there isn't a way I can get away with using the typical
> monadic bind syntax while keeping the RValues off the heap; if I'm wrong,
> and I can skip the whole withObject/HOF approach, I'd love to know it! I'm
> not (yet) familiar enough with the compiler / language spec to know about
> such guarantees, should they exist...
>
> - Charles
>
> _______________________________________________
> Haskell-Cafe mailing list
> Haskell-Cafe at haskell.org
> http://www.haskell.org/mailman/listinfo/haskell-cafe
>


More information about the Haskell-Cafe mailing list