ruby拡張で作ったクラスで使う自前アロケータとフリーの呼び方

世間の皆々様に至ってはきっと日々ruby拡張を作ったりしてることと思います。
わたしはなんかRuby/SDL使った画像ビューアを作ったりしてました。その適当な画像ビューアはまあどうでもいいんですけど、書いててふと「あれこれRuby/SDLが確保したSDL資源(Surfaceとか)どこで解放してんの?」と思ってソース読んだらGC呼ばれるときちゃんと一緒に解放されるようなので、うんよくできてるなーと思いました。
というわけでその理解が合っているのか、ruby拡張で自前のアロケータとフリーするminimalなコードのテストをしてみんとす。


mystruct.c

#include "ruby.h"

typedef struct {
    int dummy;
} MyStruct;

static VALUE cStruct = Qnil;

static void myfree(MyStruct *buf) {
    static int count = 0;
    printf("call myfree: %d\n", ++count);
    xfree(buf);
}
static VALUE myalloc(VALUE klass) {
    static int count = 0;
    MyStruct *buf = ALLOC(MyStruct);
    printf("call myalloc: %d\n", ++count);
    return Data_Wrap_Struct(klass, 0, myfree, buf);
}

void Init_mystruct() {
    cStruct = rb_define_class("MyStruct", rb_cObject);
    rb_define_alloc_func(cStruct, myalloc);
}

require 'mystruct'でInit_mystruct()が呼ばれ、MyStructクラスをrubyに提供する拡張機能です。
ALLOCというのはruby.hで定義されてるxmallocを呼び出すマクロで、xmallocの実体はruby_xmalloc、xfreeの実体はruby_xfreeです。FREEというマクロは何故か提供されていない。


ビルドにはextconf.rb

require 'mkmf'
create_makefile 'mystruct'

を用意してあげて

$ ls
extconf.rb  mystruct.c  test.rb
$ ruby extconf.rb
creating Makefile
$ ls
Makefile  extconf.rb  mystruct.c  test.rb

と作ったMakefileでビルド

$ make
cc -I. -I/usr/lib/ruby/1.8/i486-linux -I/usr/lib/ruby/1.8/i486-linux -I. -D_FILE_OFFSET_BITS=64  -fPIC -fno-strict-aliasing -g -g -O2  -fPIC   -c mystruct.c
cc -shared -o mystruct.so mystruct.o -L. -L/usr/lib -L.  -rdynamic -Wl,-export-dynamic    -lruby1.8  -lpthread -ldl -lcrypt -lm   -lc
$ ls
Makefile  extconf.rb  mystruct.c  mystruct.o  mystruct.so*  test.rb

mystruct.soができたのでテストコードtest.rbを走らせてみる。


test.rb

#!/usr/bin/ruby

require 'mystruct'
3.times do
    a = MyStruct.new
end
GC.start
puts 'end'
$ ruby test.rb
call myalloc: 1
call myalloc: 2
call myalloc: 3
call myfree: 1
call myfree: 2
call myfree: 3
end

GCが呼ばれるタイミングでMyStructが解放され、自前で用意したmyfreeが呼ばれてますね。安心。

test