A few questions about Ruby/DL

I’ve had quite good luck using Ruby/DL to wrap a shared library, but
the lack of documentation in english is possibly preventing me from
using it in the best way.

I have a few questions:

  1. Memory management – how do I free memory? When do I need to free
    memory? I can’t seem to find any free’s in the examples, but
    there are references to setting a freeing function in the PtrData
    class documentation (ext/dl/doc/dl.txt)

  2. What’s the best way to handle pointer outputs which allocate
    memory? I’ve done things like the following, but I’m not sure
    that it’s the best way:

    say the C interface has a prototype like

    int get_data(char **data, int *len);

    on success, memory is allocated for a string and the pointer is

    assigned to the data parameter. The length of the string is

    written into the len parameter.

    data = DL::malloc(DL::sizeof(“P”))
    len = DL::malloc(DL::sizeof(“L”))
    len.struct!(“L”, “val”)

    ret = LIB.get_data(data, len)

    if ret == LIB::STATUS_OK
    puts "got a string of length #{len[:val]}: #{data.ptr}"
    else
    puts "failed to get data: #{LIB::strerror(ret)}"
    end

So, my questions are:

in the above example, is there a more elegant way to deal with output
strings/data structures, etc, and how to I free the memory associated
with the output string? Is there any way for GC to know if the
memory is allocated dynamically or statically? What if the library
is responsible for freeing the memory? (clearly, I don’t know enough
about GC)

Thanks for the help,

···


Josh Huber

Josh Huber wrote:

I’ve had quite good luck using Ruby/DL to wrap a shared library, but
the lack of documentation in english is possibly preventing me from
using it in the best way.

I have a few questions:

  1. Memory management – how do I free memory? When do I need to free
    memory? I can’t seem to find any free’s in the examples, but
    there are references to setting a freeing function in the PtrData
    class documentation (ext/dl/doc/dl.txt)

You don’t have to free memory yourself. With Ruby/DL you are dealing
with DL::PtrData objects that basically represent a memory pointer. When
there are no references to a PtrData object left, GC will collect this
object and invoke its free routine, which takes care of the allocated
memory. Take a look at PtrData#free attribute. BTW, if you know that
your library function returns a pointer to some kind of static buffer
and you don’t want a PtrData object release this buffer, just set its
free attribute to nil.

  1. What’s the best way to handle pointer outputs which allocate
    memory? I’ve done things like the following, but I’m not sure
    that it’s the best way:

    say the C interface has a prototype like

    int get_data(char **data, int *len);

    on success, memory is allocated for a string and the pointer is

    assigned to the data parameter. The length of the string is

    written into the len parameter.

    data = DL::malloc(DL::sizeof(“P”))
    len = DL::malloc(DL::sizeof(“L”))
    len.struct!(“L”, “val”)

    ret = LIB.get_data(data, len)

    if ret == LIB::STATUS_OK
    puts “got a string of length #{len[:val]}: #{data.ptr}”
    else
    puts “failed to get data: #{LIB::strerror(ret)}”
    end

I would do it like that:

$ cat test.c
#include <stdio.h>

int get_data(char** data, int* len) {
int i;
len = 30;
data = (char)malloc(sizeof(char)
(*len));
sprintf(data, “string of size %d”, len);
return (len);
}
$ cc -dynamic -bundle -o test.bundle test.c
$ irb
irb(main):001:0> require ‘dl/import’
=> true
irb(main):002:0> module TestDLL
irb(main):003:1> extend DL::Importable
irb(main):004:1> dlload ‘test.bundle’
irb(main):005:1> extern "int get_data(char
, int
)"
irb(main):006:1> end
=> #<DL::Symbol:0x0x4c9910 func=0x0x1e6f28 ‘int get_data(void *, void *);’>
irb(main):007:0> data = DL::PtrData.new(0)
=> #<DL::PtrData:0x0x4905f0 ptr=0x0x0 size=0 free=0x0x0>
irb(main):008:0> data.free = DL::FREE
=> #<DL::Symbol:0x0x456120 func=0x0x5876ec ‘void free(void *);’>
irb(main):009:0> len = DL::PtrData.malloc(DL.sizeof(‘I’))
=> #<DL::PtrData:0x0x441f30 ptr=0x0x441710 size=4 free=0x0x0>
irb(main):010:0> len.struct!(‘I’, ‘val’)
=> nil
irb(main):011:0> TestDLL.get_data(data.ref, len)
=> 30
irb(main):012:0> len[‘val’]
=> 30
irb(main):013:0> data.to_s
=> “string of size 30”
irb(main):014:0>

/kent

“Kent S.” happy@user.com writes:

You don’t have to free memory yourself. With Ruby/DL you are dealing
with DL::PtrData objects that basically represent a memory
pointer. When there are no references to a PtrData object left, GC
will collect this object and invoke its free routine, which takes
care of the allocated memory. Take a look at PtrData#free
attribute. BTW, if you know that your library function returns a
pointer to some kind of static buffer and you don’t want a PtrData
object release this buffer, just set its free attribute to nil.

Okay, but it looks like by default PtrData#free is nil, right? Do I
always have to specify a freeing function?

It seems as though the result of DL::malloc also does not have the
#free attribute set by default.

irb(main):001:0> require ‘dl/import’
=> true
irb(main):002:0> module TestDLL
irb(main):003:1> extend DL::Importable
irb(main):004:1> dlload ‘test.bundle’
irb(main):005:1> extern “int get_data(char**, int*)”
irb(main):006:1> end
=> #<DL::Symbol:0x0x4c9910 func=0x0x1e6f28 ‘int get_data(void *, void *);’>
irb(main):007:0> data = DL::PtrData.new(0)
=> #<DL::PtrData:0x0x4905f0 ptr=0x0x0 size=0 free=0x0x0>
irb(main):008:0> data.free = DL::FREE
=> #<DL::Symbol:0x0x456120 func=0x0x5876ec ‘void free(void *);’>
irb(main):009:0> len = DL::PtrData.malloc(DL.sizeof(‘I’))
=> #<DL::PtrData:0x0x441f30 ptr=0x0x441710 size=4 free=0x0x0>
irb(main):010:0> len.struct!(‘I’, ‘val’)
=> nil
irb(main):011:0> TestDLL.get_data(data.ref, len)
=> 30
irb(main):012:0> len[‘val’]
=> 30
irb(main):013:0> data.to_s
=> “string of size 30”
irb(main):014:0>

Thanks for this example, it’s a little more clear now. Wouldn’t you
want to set len.free to DL::FREE as well?

···


Josh Huber

Josh Huber wrote:

Okay, but it looks like by default PtrData#free is nil, right? Do I
always have to specify a freeing function?

It seems as though the result of DL::malloc also does not have the
#free attribute set by default.

Yes, you’re right. I should have used DL.malloc instead of
PtrData.malloc. DL.malloc sets free function by default:

$ irb
irb(main):001:0> require ‘dl/import’
=> true
irb(main):002:0> ptr = DL.malloc(DL.sizeof(‘I’))
=> #<DL::PtrData:0x0x44d2e0 ptr=0x0x484ba0 size=4 free=0x0x5876ec>
irb(main):003:0> ptr.free
=> #<DL::Symbol:0x0x4551f0 func=0x0x5876ec ‘void (free)(void *);’>
irb(main):004:0>

Thanks for this example, it’s a little more clear now. Wouldn’t you
want to set len.free to DL::FREE as well?

I should have used len = DL.malloc(DL.malloc(‘I’)) instead.

/kent

Oops, I meant

len = DL.malloc(DL.sizeof(‘I’))

/kent

···

On Mar 11, 2004, at 2:49 PM, Kent S. wrote:

Thanks for this example, it’s a little more clear now. Wouldn’t you
want to set len.free to DL::FREE as well?

I should have used len = DL.malloc(DL.malloc(‘I’)) instead.

/kent

Kent Sibilev ksibilev@bellsouth.net writes:

Oops, I meant

len = DL.malloc(DL.sizeof(‘I’))

Ah, excellent. Thanks for the help!

···


Josh Huber