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が呼ばれてますね。安心。