Home > programming > D & Ruby FFI, part 1: mapping D structs onto Ruby classes

D & Ruby FFI, part 1: mapping D structs onto Ruby classes

This post starts series about interaction between D and Ruby via FFI. Several things are to be investigated.

Firstly, both languages have their own garbage collectors. Although we can disable D garbage collector manually for some classes/structs, that’s not convenient at all. We want to use existing D code without modifying it.

Also, D ABI is a bit more sophisticated than C ABI (some documentation is at http://dlang.org/abi.html). So we need to understand what is the layout of D objects and how to work with it effectively.


This post describes how to deal with garbage collection issues.

The method suggested below currently works with structs only(Although, structs can be passed by reference in D, and they are more lightweight than classes, so it’s not a big limitation.)

Suppose we want to create D struct from Ruby. The memory occupied by the struct can’t be managed by D garbage collector, because it’s the Ruby code which is to manage the memory, not D code. D functions can only hide some details of allocating/deallocating memory.

Let’s take this struct as an example:

public struct Foo {
    public string hello = "hello, world!";
}

We have to provide two functions for alloc/free following C calling conventions:

extern (C) void* foo_new() {
    Foo* p = cast(Foo*)malloc(Foo.sizeof);
    std.conv.emplace(p);

    GC.addRange(p, Foo.sizeof);
    return p;
}

extern (C) void foo_free(Foo* p) {
    GC.removeRange(p);
    free(cast(void*)p);
}

Allocating memory is easy – one calls corresponding external function via FFI, gets a pointer, and that’s it. Deallocation is a little bit trickier, since Ruby also has its own GC. Therefore, when we create the instance, we should also register the object’s finalizer which will be called by GC after the instance destruction. Notice this word, ‘after’. It means that we must copy the pointer to somewhere, otherwise GC just won’t be able to destroy the object because the finalizer will reference the object itself. (See http://www.mikeperham.com/2010/02/24/the-trouble-with-ruby-finalizers/ where this subtle thing is described in detail.)

So the source code of the Ruby part is something like this:

require 'ffi'

module MyLibrary
    extend FFI::Library
    ffi_lib './test.so'
    attach_function :foo_new, [], :pointer
    attach_function :foo_free, [:pointer], :void
end

class Foo
    def initialize
        @ptr = MyLibrary.foo_new
        ObjectSpace.define_finalizer @ptr, Foo.finalize(@ptr)
    end

    def self.finalize ptr
        proc { MyLibrary.foo_free ptr }
    end
end

Here I visualised the process of interaction of the two languages. You may open in another tab this gist to see all the code.

Another subtle thing is, how do we initialize D garbage collector? In order for it to work, we are to call core.runtime.Runtime.initialize, and when we’re done with the library, we are to call terminate() method

Theoretically, one could use “static this() { … }” and “static ~this() { … }” in D code. Practically, it doesn’t work yet. Thus we are forced to resort to some dirty tricks with gcc function attributes, namely, with __attribute__((constructor))/__attribute__((destructor)). (Read the discussion at http://stackoverflow.com/questions/9759880/automatically-executed-functions-when-loading-shared-libraries).

UPD: dirty tricks mentioned above can cause segfaults for no apparent reason. You better manually call the function initializing runtime, through FFI.

Advertisements
Categories: programming Tags: , ,
  1. No comments yet.
  1. No trackbacks yet.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: