extending/embedding ruby (callbacks) [LONG]

P

Peter Schrammel

Hi,

I try to implement an interface for fuse with ruby but fail to a strange
error.

You probably won't try this code because you'd have to install fuse to
test it so I give some comments:

I'd like to implement this as an extension so the testcode should look
like this:

#!/usr/bin/ruby
require("RFuse")

class RFuse
def getdir(path,filler)
Dir.foreach(path) {|x| filler.push(x,8)}
end
def getattr(path)
end
end

fo=RFuse.new
begin
fo.main
rescue
f=File.new("/tmp/error","w+")
f.puts "Error:" + $!
f.close
end

The user should just extend the class with some methods which will be
called back by fuse. So I have to wrap the callbacks. The interesting
line is at ***.

#ifdef linux
/* For pread()/pwrite() */
#define _XOPEN_SOURCE 500
#endif
//FOR LINUX ONLY
#include <linux/stat.h>

#include <ruby.h>
#include <fuse.h>
#include <errno.h>
#include <sys/statfs.h>
#ifdef HAVE_SETXATTR
#include <sys/xattr.h>
#endif


//This is a wrapper around the filler callback function
struct fill_t {
fuse_dirfil_t filler;
fuse_dirh_t handler;
};

static VALUE rfill_new(VALUE class){
VALUE self;
struct fill_t *filler;
self = Data_Make_Struct(class, struct fill_t, 0,free,filler);
return self;
}

static VALUE rfill_push(VALUE self,VALUE name, VALUE type) {
struct fill_t *fill;
Data_Get_Struct(self,struct fill_t,fill);
fill->filler(fill->handler,STR2CSTR(name),NUM2INT(type));
return self;
}

//------------------


static VALUE global_self; //TODO: have to avoid global vars

//call getdir with that an RFiller object
static int rf_getdir(const char *path, fuse_dirh_t h, fuse_dirfil_t f)
{
VALUE rfiller_class;
VALUE rfiller_instance;
struct fill_t *fillerc;
rfiller_class=rb_const_get(rb_cObject,rb_intern("RFiller"));

//***the following line seems to be the problem. If I comment everything
out between here and "return 0" it runs okay... though nothing happens.

rfiller_instance=rb_funcall(rfiller_class,rb_intern("new"),0);
//BTW: I can't do the rb_class_new_instance here STRANGE!

//***commenting out from here gives me strange errors (see bellow)

rb_gc_register_address(&rfiller_instance);//Do I need this?
Data_Get_Struct(rfiller_instance,struct fill_t,fillerc);
fillerc->filler=f;//Init the filler by hand....not nice...
fillerc->handler=h;
rb_funcall(global_self,rb_intern("getdir"),2,rb_str_new2(path),rfiller_instance);

//destroy the filler...
rb_gc_unregister_address(&rfiller_instance);
return 0;
}

//calls getattr with path and expects a File::Stat back
static int rf_getattr(const char *path, struct stat *stbuf)
{
int res; //For testing only
res = lstat(path, stbuf);
return 0;
}

static VALUE rf_main(VALUE self){
char* argv[2];
int ret;
argv[0]="test-rfuse";
argv[1]="/tmp/fuse";
struct fuse_operations *fuseop;

Data_Get_Struct(self,struct fuse_operations,fuseop);
ret = fuse_main(2,argv,fuseop);
return Qnil;
}

static VALUE rf_init(VALUE self){
return self;
}

static VALUE rf_new(VALUE class){
VALUE self;
struct fuse_operations *fuseop;
self = Data_Make_Struct(class, struct fuse_operations, 0,free,fuseop);
fuseop->getattr=rf_getattr;
fuseop->getdir=rf_getdir;
rb_obj_call_init(self,0,0);
global_self=self; //TODO: hide this...
return self;
}

void Init_RFuse() {
VALUE cRFuse=rb_define_class("RFuse",rb_cObject);
rb_define_singleton_method(cRFuse,"new",rf_new,0);
rb_define_method(cRFuse,"initialize",rf_init,0);
rb_define_method(cRFuse,"main",rf_main,0);

VALUE cRFiller=rb_define_class("RFiller",rb_cObject);
rb_define_singleton_method(cRFiller,"new",rfill_new,0);
//rb_define_method(cRFiller,"initialize",rfill_init,0);
rb_define_method(cRFiller,"push",rfill_push,2);
// rb_define_method(cRFiller,"test",rfill_test,0);
}

After starting the progam and doing some (=up to 25) "ls /tmp/fuse" the
programm crashes
cat /tmp/error says:
Error:stack level too deep

I think that I missed some cleanupcode...

Help I very much appreciated (my C isn't the best).
The interface of fuse is at:
http://cvs.sourceforge.net/viewcvs.py/fuse/fuse/include/fuse.h?rev=1.61&view=auto


Bye

Peter
 
C

Charles Mills

Peter said:
(...)
I'd like to implement this as an extension so the testcode should look
like this:

It may help if you explain what your trying to do a bit more.
#!/usr/bin/ruby
require("RFuse")

class RFuse
def getdir(path,filler)
Dir.foreach(path) {|x| filler.push(x,8)}
end
def getattr(path)
end
end

fo=RFuse.new
begin
fo.main
rescue
f=File.new("/tmp/error","w+")
f.puts "Error:" + $!
f.close
end

The user should just extend the class with some methods which will be
called back by fuse. So I have to wrap the callbacks. The interesting
line is at ***.

#ifdef linux
/* For pread()/pwrite() */
#define _XOPEN_SOURCE 500
#endif
//FOR LINUX ONLY
#include <linux/stat.h>

#include <ruby.h>
#include <fuse.h>
#include <errno.h>
#include <sys/statfs.h>
#ifdef HAVE_SETXATTR
#include <sys/xattr.h>
#endif


//This is a wrapper around the filler callback function
struct fill_t {
fuse_dirfil_t filler;
fuse_dirh_t handler;
};

the function below should be registered using rb_define_alloc_func()
and is typically called rfill_alloc()
static VALUE rfill_new(VALUE class){
VALUE self;
struct fill_t *filler;
self = Data_Make_Struct(class, struct fill_t, 0,free,filler);
return self;
}

static VALUE rfill_push(VALUE self,VALUE name, VALUE type) {
struct fill_t *fill;
Data_Get_Struct(self,struct fill_t,fill);
fill->filler(fill->handler,STR2CSTR(name),NUM2INT(type));
return self;
}

//------------------


static VALUE global_self; //TODO: have to avoid global vars

//call getdir with that an RFiller object
static int rf_getdir(const char *path, fuse_dirh_t h, fuse_dirfil_t f)
{
VALUE rfiller_class;
VALUE rfiller_instance;
struct fill_t *fillerc;
rfiller_class=rb_const_get(rb_cObject,rb_intern("RFiller"));

//***the following line seems to be the problem. If I comment everything
out between here and "return 0" it runs okay... though nothing happens.

*** see above ***
rfiller_instance=rb_funcall(rfiller_class,rb_intern("new"),0);
//BTW: I can't do the rb_class_new_instance here STRANGE!

//***commenting out from here gives me strange errors (see bellow)

you don't need this, if your worried about rfiller_instance being GC'ed
make it volatile.
rb_gc_register_address(&rfiller_instance);//Do I need this?
Data_Get_Struct(rfiller_instance,struct fill_t,fillerc);
fillerc->filler=f;//Init the filler by hand....not nice...
fillerc->handler=h;

not sure how the flow of control goes in your program but seems pretty
obvious you have infinite recursion going on right here.
rb_funcall(global_self,rb_intern("getdir"),2,rb_str_new2(path),rfiller_instance);

don't need this either
//destroy the filler...
rb_gc_unregister_address(&rfiller_instance);
return 0;
}
(...)

void Init_RFuse() {
VALUE cRFuse=rb_define_class("RFuse",rb_cObject);

should be using rb_define_alloc_func() as noted above
rb_define_singleton_method(cRFuse,"new",rf_new,0);
rb_define_method(cRFuse,"initialize",rf_init,0);
rb_define_method(cRFuse,"main",rf_main,0);

VALUE cRFiller=rb_define_class("RFiller",rb_cObject);
''

rb_define_singleton_method(cRFiller,"new",rfill_new,0);
//rb_define_method(cRFiller,"initialize",rfill_init,0);
rb_define_method(cRFiller,"push",rfill_push,2);
// rb_define_method(cRFiller,"test",rfill_test,0);
}
(...)

-Charlie
 
P

Peter Schrammel

Charles said:
Peter Schrammel wrote:
It may help if you explain what your trying to do a bit more.
OK:
The ruby part just does one thing:
extend the RFuse Class with some methods:
getdir
getattr

Then it creates a RFuse object a calls main on it.

main is in C: It registers the wrapper functions "rf_getdir",
"rf_getattr" to fuse and calls the main function of fuse, so the ruby
programm is sleeping and waiting.

if somebody does a "ls /tmp/fuse" the fuse system calls the registered
function rf_getdir with 3 args:
path, handler, filler
where
path is the requested path
handler is a hanlde used by filler
filler is a function, that takes 3 args: handler,string,mode

now rf_getdir should do the following:
create an object of RFiller(set the filler fuction and the handler) and
pass the path, and the Rfiller object to self.getdir (on the ruby side).

self.getdir calls filler.push("filename",mode) and returns

the RFiller object should be destroyed now and rf_getdir is done.

The problem is:
even if I don't call ruby's getdir method I have endless recursions.
the function below should be registered using rb_define_alloc_func()
and is typically called rfill_alloc()

done that...thanks
you don't need this, if your worried about rfiller_instance being GC'ed
make it volatile.

how to do this? sorry my ruby extension programming is at newbie level :)
not sure how the flow of control goes in your program but seems pretty
obvious you have infinite recursion going on right here.
ok: But if I do just this:
static int rf_getdir(const char *path, fuse_dirh_t h, fuse_dirfil_t f)
{
VALUE rfiller_class;
VALUE rfiller_instance;
rfiller_class=rb_const_get(rb_cObject,rb_intern("RFiller"));
rfiller_instance=rb_funcall(rfiller_class,rb_intern("new"),0);
// no call to anything!
return 0;
}

I still get the same error... :-(
Commenting out the "rfiller_instance="... line everything is all right.

To me it seems the GC wakes up one in a while does something ...
strange. Or I run out of memory because the GC never wakes up.


Peter
 
C

Charles Mills

Peter said:
OK:
The ruby part just does one thing:
extend the RFuse Class with some methods:
getdir
getattr

Then it creates a RFuse object a calls main on it.

main is in C: It registers the wrapper functions "rf_getdir",
"rf_getattr" to fuse and calls the main function of fuse, so the ruby
programm is sleeping and waiting.

if somebody does a "ls /tmp/fuse" the fuse system calls the registered
function rf_getdir with 3 args:
path, handler, filler
where
path is the requested path
handler is a hanlde used by filler
filler is a function, that takes 3 args: handler,string,mode

now rf_getdir should do the following:
create an object of RFiller(set the filler fuction and the handler) and
pass the path, and the Rfiller object to self.getdir (on the ruby side).

self.getdir calls filler.push("filename",mode) and returns

the RFiller object should be destroyed now and rf_getdir is done.

The problem is:
even if I don't call ruby's getdir method I have endless recursions.


done that...thanks


how to do this? sorry my ruby extension programming is at newbie
level :)

no worries. you should read this:
http://www.rubygarden.com/ruby/ruby?action=browse&id=GCAndExtensions

volatile is a C keyword/qualifier.
ok: But if I do just this:
static int rf_getdir(const char *path, fuse_dirh_t h, fuse_dirfil_t f)
{
VALUE rfiller_class;
VALUE rfiller_instance;
rfiller_class=rb_const_get(rb_cObject,rb_intern("RFiller"));
rfiller_instance=rb_funcall(rfiller_class,rb_intern("new"),0);
// no call to anything!
return 0;
}

I still get the same error... :-(
Commenting out the "rfiller_instance="... line everything is all right.

To me it seems the GC wakes up one in a while does something ...
strange. Or I run out of memory because the GC never wakes up.

Maybe you should start with something smaller. Probably something that
doesn't use function pointers. Perhaps there is a way to test the
classes individually... or you could add some sanity tests.

-Charlie
 
T

ts

try it with

P> static VALUE rf_main(VALUE self){
P> char* argv[2];
P> int ret;
P> argv[0]="test-rfuse";
P> argv[1]="/tmp/fuse";

char* argv[3];
int ret;
argv[0]="-s";
argv[1]="test-rfuse";
argv[2]="/tmp/fuse";

P> struct fuse_operations *fuseop;

P> Data_Get_Struct(self,struct fuse_operations,fuseop);
P> ret = fuse_main(2,argv,fuseop);

ret = fuse_main(3,argv,fuseop);

P> return Qnil;
P> }

or something like this


Guy Decoux
 
T

ts

This is wrong :-(((

t> argv[0]="-s";
t> argv[1]="test-rfuse";
t> argv[2]="/tmp/fuse";

argv[0]="test-rfuse";
argv[1]="/tmp/fuse";
argv[2]="-s";

Guy Decoux
 
P

Peter Schrammel

ts said:
This is wrong :-(((

t> argv[0]="-s";
t> argv[1]="test-rfuse";
t> argv[2]="/tmp/fuse";

argv[0]="test-rfuse";
argv[1]="/tmp/fuse";
argv[2]="-s";

Guy Decoux

Yes... that's it. It wasn't ruby's fault at all but some kind of race
conditions with multithreaded FUSE. Thanks everybody.

Peter
 
A

Asbjørn Reglund Thorsen

---259931839-1996686507-1105794351=:19693
Content-Type: MULTIPART/MIXED; BOUNDARY="-259931839-1996686507-1105794351=:19693"

This message is in MIME format. The first part should be readable text,
while the remaining parts are likely unreadable without MIME-aware tools.

---259931839-1996686507-1105794351=:19693
Content-Type: TEXT/PLAIN; charset=ISO-8859-1; format=flowed
Content-Transfer-Encoding: QUOTED-PRINTABLE


I`m having trouble with an interface to a c++ library. It seems like=20
Ruby`s GC is collecting objects that it shouldn`t. I have read the=20
SWIG-Ruby
documentation on the matter, but I am unsure of where to put the markfunc=
=20
, and how to get it to interact with my program.

My interface file : mHeat1.i
My Ruby code : run.rb

require 'mHeat1'

menu =3D MHeat1::MenuSystem.new
menu.init("Ruby Interface", "This is so cool !")
heat =3D MHeat1::Heat1.new
heat.define(menu)
heat.scan
heat.solveProblem
heat.resultReport

The problem is the MenuSystem, I found out by using gdb:
gdb ruby
(gdb)run run.rb

=2E..

Program received signal SIGSEGV, Segmentation fault.
[Switching to Thread 16384 (LWP 1345)]
0x00000089 in ?? ()
(gdb) where
#0 0x00000089 in ?? ()
#1 0x40388c19 in free_MenuSystem () from ./mHeat1.so
#2 0x0806f636 in rb_gc_call_finalizer_at_exit () at gc.c:1858
#3 0x08053e34 in ruby_finalize_1 () at eval.c:1418
#4 0x08053f43 in ruby_cleanup (ex=3D0) at eval.c:1453
#5 0x08054081 in ruby_stop (ex=3D135573240) at eval.c:1484
#6 0x080540ef in ruby_run () at eval.c:1505
#7 0x08052245 in main (argc=3D135573240, argv=3D0x814aef8, envp=3D0xbffff0=
20)
at main.c:46

Is it possible to use the %marcfunc directive in this case ? And how ?
I am a n00b at this, so I`d really like some help/tips.

</asbj=F8rn>
---259931839-1996686507-1105794351=:19693--
---259931839-1996686507-1105794351=:19693--
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,755
Messages
2,569,536
Members
45,014
Latest member
BiancaFix3

Latest Threads

Top