Doge log

Abby CTO 雑賀 力王のオフィシャルサイトです

RubyInline

RubyInlineを使うとCを埋め込めるらしいので試してみた。

$:.unshift File.dirname(__FILE__)

require 'rubygems'
require 'inline'

VERSION = '0.0.1'

class KQueue
  inline do |builder|
    builder.include "<sys/event.h>"
    builder.include "<sys/time.h>"
    builder.include "<unistd.h>"
    builder.include "<errno.h>"
   
=begin
    builder.map_c_const({
	    'EVFILT_READ' => 'int',
	    'EVFILT_WRITE' => 'int',
	    'EVFILT_AIO' => 'int',
	    'EVFILT_VNODE' => 'int',
	    'EVFILT_PROC' => 'int',
	    'EVFILT_SIGNAL' => 'int',
	    'EVFILT_TIMER' => 'int',
      'EV_ADD'      => 'int',
      'EV_ENABLE'   => 'int',
      'EV_DISABLE'  => 'int',
      'EV_DELETE'   => 'int',
      'EV_ONESHOT'  => 'int',
      'EV_CLEAR'    => 'int',
      'EV_EOF'      => 'int',
      'NOTE_LOWAT'  => 'int',
	    'NOTE_DELETE' => 'int',
	    'NOTE_WRITE' => 'int',
	    'NOTE_EXTEND' => 'int',
	    'NOTE_ATTRIB' => 'int',
	    'NOTE_LINK' => 'int',
	    'NOTE_RENAME' => 'int',
	    'NOTE_REVOKE' => 'int',
      'NOTE_EXIT'  => 'int',   
	    'NOTE_FORK' => 'int', 
	    'NOTE_EXEC' => 'int',
	    'NOTE_PCTRLMASK' => 'int',
	    'NOTE_PDATAMASK' => 'int',
      'NOTE_TRACK' => 'int',
	    'NOTE_CHILD' => 'int',
	    'NOTE_TRACKERR' => 'int',
   })
=end
      
    builder.prefix <<-"END"
      #define Mem_New(type, n) \
      n == 0 ? NULL : \
      (type *)malloc((n) * sizeof(type))

      static VALUE rb_cKQueue, rb_cKEvent;

      struct keventdata
      {
        struct kevent e;
      };
      
      static int 
      get_kqueue_fd(VALUE obj)
      {
        VALUE fd;
        fd = rb_iv_get(obj, "@fileno");
        return NUM2INT(fd);
      };

      static void
      free_kqueue(VALUE obj)
      {
        int fd;
        if (obj) {
          fd = get_kqueue_fd(obj);
          close(fd);
          xfree(obj);
        }
      };
      
      static void
      free_kevent(struct keventdata *kev)
      {
        if (kev) {
          xfree(kev);
        }
      };

      static VALUE
      fkqueue_alloc(VALUE klass)
      {
        return Data_Wrap_Struct(klass, 0, free_kqueue, 0);
      };
      
      static VALUE
      fkevent_alloc(VALUE klass)
      {
        return Data_Wrap_Struct(klass, 0, free_kevent, 0);
      };
      
      static void
      set_kevent_flags(VALUE obj, struct kevent kev)
      {
        rb_iv_set(obj, "@ident", INT2NUM(kev.ident));
        rb_iv_set(obj, "@filter", INT2NUM(kev.filter));
        rb_iv_set(obj, "@flags", INT2NUM(kev.flags));
        rb_iv_set(obj, "@fflags", INT2NUM(kev.fflags));
        rb_iv_set(obj, "@data", INT2NUM(kev.data));
      };
      
      static VALUE
      fkqueue_initialize(VALUE obj)
      {
        int kq_fd = kqueue();
        if(kq_fd == -1){
          rb_raise(rb_eStandardError, "kqueue initilization failed");
          goto error;
        }
        rb_iv_set(obj, "@fileno", INT2NUM(kq_fd));
        return obj;
        
        error:
        return Qnil;
      };
      
      
      static VALUE
      fkevent_initialize(int argc, VALUE *argv, VALUE obj)
      {
        struct keventdata *kev;
        struct kevent ev;
        VALUE vident,vfilter,vflags,vfflags,vdata,vudata;
        int ident;
        int filter = EVFILT_READ;
        int flags = EV_ADD;
        int fflags = 0;
        int data = 0;
        void *udata = NULL; 
        
        
        rb_scan_args(argc, argv, "15", &vident, &vfilter, &vflags, &vfflags, &vdata, &vudata );
        ident = NUM2INT(vident);
        if(!NIL_P(vfilter))
          filter = NUM2INT(vfilter);
        if(!NIL_P(vflags))
          flags = NUM2INT(vflags);
        if(!NIL_P(vfflags))
          fflags = NUM2INT(vfflags);
        if(!NIL_P(vdata))
          data = NUM2INT(vdata);    

        EV_SET(&ev, ident, filter, flags, fflags, data, udata);

        kev = ALLOC(struct keventdata);
        DATA_PTR(obj) = kev;
        kev->e = ev;
        set_kevent_flags(obj, ev);
        return obj;
      };
      
      static struct kevent
      get_kevent(VALUE obj)
      {
        struct keventdata *data;
        Data_Get_Struct(obj, struct keventdata, data);
        return data->e;
      };

      static VALUE
      fkqueue_control(int argc, VALUE *argv, VALUE obj)
      {
        int i, n;
        int kq = get_kqueue_fd(obj);
        int nchanges = 0;
        int nevents = 0;
        struct kevent *chl = NULL;
        struct kevent *evl = NULL;
      	struct timespec timeoutspec;
	struct timespec *ptimeoutspec;
        VALUE changelist, maxevents, vtimeout, result;

        rb_scan_args(argc, argv, "21", &changelist, &maxevents, &vtimeout );
        
        if(!NIL_P(vtimeout)){
          Check_Type(vtimeout, T_FIXNUM);
          double timeout;
          long seconds;
          
          timeout = NUM2DBL(vtimeout);
          seconds = (long)timeout;
          timeout = timeout - (double)seconds;
          timeoutspec.tv_sec = seconds;
	  timeoutspec.tv_nsec = (long)(timeout * 1E9);
	  ptimeoutspec = &timeoutspec;
        }else{
          ptimeoutspec = NULL;
        }

        if(!NIL_P(changelist)){
          Check_Type(changelist, T_ARRAY);
          nchanges = RARRAY_LEN(changelist);
          chl = Mem_New(struct kevent, RARRAY_LEN(changelist));
          
          for(i = 0; i < RARRAY_LEN(changelist); i++){
            chl[i] = get_kevent(RARRAY_PTR(changelist)[i]);
          }
        }
        nevents = NUM2INT(maxevents);
        evl = Mem_New(struct kevent, nevents);

        n = kevent(kq, chl, nchanges, evl, nevents, ptimeoutspec);
        
        if(n == -1){
          rb_raise(rb_eStandardError, "kevent Bad Descriptor");
          goto error;
        }

        result = rb_ary_new();
        if(result == -1){
          goto error;
        }

        for(i = 0; i < n; i++){
          VALUE kevent_object;
          struct keventdata *kev;
          
          kevent_object = fkevent_alloc(rb_cKEvent);
          kev = ALLOC(struct keventdata);
          DATA_PTR(kevent_object) = kev;
          kev->e = evl[i];
          set_kevent_flags(kevent_object, kev->e);
          rb_ary_push(result, kevent_object);
        }

        free(chl);
        free(evl);
        
        return result;

        error:
        free(chl);
        free(evl);

        return Qnil;
      };

      static VALUE
      fkevent_to_s(VALUE obj)
      {
        int ident, filter, flags, fflags, data;
        ident = rb_iv_get(obj, "@ident");
        filter = rb_iv_get(obj, "@filter");
        flags = rb_iv_get(obj, "@flags");
        fflags = rb_iv_get(obj, "@fflags");
        data = rb_iv_get(obj, "@data");
        return rb_sprintf("<KEvent ident=%d filter=%d flags=%d fflags=%d data=%d>", \
                          ident,
                          filter,
                          flags,
                          fflags,
                          data);
      };

    END
    
    builder.add_to_init <<-"END"
    rb_cKEvent = rb_define_class("KEvent", rb_cObject);
    rb_define_alloc_func(rb_cKEvent, fkevent_alloc);
    rb_define_method(rb_cKEvent, "initialize", fkevent_initialize, -1);
    rb_define_method(rb_cKEvent, "to_s", fkevent_to_s, 0);
    rb_define_attr(rb_cKEvent, "ident", 1, 1);
    rb_define_attr(rb_cKEvent, "filter", 1, 1);
    rb_define_attr(rb_cKEvent, "flags", 1, 1);
    rb_define_attr(rb_cKEvent, "fflags", 1, 1);
    rb_define_attr(rb_cKEvent, "data", 1, 1);
    rb_define_attr(rb_cKEvent, "udata", 1, 1);

    rb_cKQueue = rb_define_class("KQueue", rb_cObject);
    rb_define_alloc_func(rb_cKQueue, fkqueue_alloc);
    rb_define_method(rb_cKQueue, "initialize", fkqueue_initialize, 0);
    rb_define_method(rb_cKQueue, "control", fkqueue_control, -1);
    rb_define_attr(rb_cKQueue, "fileno", 1, 0);
      
    rb_define_global_const("EVFILT_READ", INT2FIX(EVFILT_READ));
    rb_define_global_const("EVFILT_WRITE", INT2FIX(EVFILT_WRITE));
    rb_define_global_const("EVFILT_AIO", INT2FIX(EVFILT_AIO));
    rb_define_global_const("EVFILT_VNODE", INT2FIX(EVFILT_VNODE));
    rb_define_global_const("EVFILT_PROC", INT2FIX(EVFILT_PROC));
    rb_define_global_const("EVFILT_SIGNAL", INT2FIX(EVFILT_SIGNAL));
    rb_define_global_const("EVFILT_TIMER", INT2FIX(EVFILT_TIMER));
    rb_define_global_const("EV_ADD", INT2FIX(EV_ADD));
    rb_define_global_const("EV_ENABLE", INT2FIX(EV_ENABLE));
    rb_define_global_const("EV_DISABLE", INT2FIX(EV_DISABLE));
    rb_define_global_const("EV_DELETE", INT2FIX(EV_DELETE));
    rb_define_global_const("EV_ONESHOT", INT2FIX(EV_ONESHOT));
    rb_define_global_const("EV_CLEAR", INT2FIX(EV_CLEAR));
    rb_define_global_const("EV_EOF", INT2FIX(EV_EOF));
    rb_define_global_const("NOTE_LOWAT", INT2FIX(NOTE_LOWAT));
    rb_define_global_const("NOTE_DELETE", INT2FIX(NOTE_DELETE));
    rb_define_global_const("NOTE_WRITE", INT2FIX(NOTE_WRITE));
    rb_define_global_const("NOTE_EXTEND", INT2FIX(NOTE_EXTEND));
    rb_define_global_const("NOTE_ATTRIB", INT2FIX(NOTE_ATTRIB));
    rb_define_global_const("NOTE_LINK", INT2FIX(NOTE_LINK));
    rb_define_global_const("NOTE_RENAME", INT2FIX(NOTE_RENAME));
    rb_define_global_const("NOTE_REVOKE", INT2FIX(NOTE_REVOKE));
    rb_define_global_const("NOTE_EXIT", INT2FIX(NOTE_EXIT));
    rb_define_global_const("NOTE_FORK", INT2FIX(NOTE_FORK));
    rb_define_global_const("NOTE_EXEC", INT2FIX(NOTE_EXEC));
    rb_define_global_const("NOTE_PCTRLMASK", INT2FIX(NOTE_PCTRLMASK));
    rb_define_global_const("NOTE_PDATAMASK", INT2FIX(NOTE_PDATAMASK));
    rb_define_global_const("NOTE_TRACK", INT2FIX(NOTE_TRACK));
    rb_define_global_const("NOTE_CHILD", INT2FIX(NOTE_CHILD));
    rb_define_global_const("NOTE_TRACKERR", INT2FIX(NOTE_TRACKERR));
      
    END


  end
  

end

初心者なのでkqueueのラッパーから書いてみることにした。
どんどん書いていくうちに普通の拡張コードになってた。。。。
RubyInlineじゃなくてもいいけどビルドが一発なのでそのままにしてる。。。

基本的にpython2.6のkqueueと同じAPIにした。よけいな事は一切しない。

    kq = KQueue.new
    f = File.open("/tmp/test")
    kevent = KEvent.new(f.to_i, EVFILT_VNODE, EV_ADD|EV_ONESHOT|EV_ENABLE, NOTE_WRITE|NOTE_DELETE) 
    kq.control([kevent], 0)
    ev = kq.control(nil, 1)
    ev.each{|item|
      p item
    }   

ぶっちゃけrubyの拡張の方がpythonに比べて奇麗に書けるし、楽。
(参照カウントの上げ下げ、実行権の許可とかいらないし)
ピンチになったらCで!っていうのが比較的楽なのでrubyに乗り換えてもいいなって気がしてくるよね。

全然話し変るけど一般的なrubyプログラマの仕事って何してるのか気になるな。
Railsばっかだったとしたら残念だな。