[lnkForumImage]
TotalShareware - Download Free Software

Confronta i prezzi di migliaia di prodotti.
Asp Forum
 Home | Login | Register | Search 


 

Forums >

comp.lang.ruby

extending/embedding ruby (callbacks) [LONG]

Peter Schrammel

1/13/2005 2:55:00 PM

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&...


Bye

Peter
7 Answers

Charles Mills

1/13/2005 4:53:00 PM

0


Peter Schrammel wrote:
>(...)
> 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

Peter Schrammel

1/13/2005 6:28:00 PM

0

Charles Mills schrieb:
> 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

Charles Mills

1/13/2005 7:02:00 PM

0


Peter Schrammel wrote:
> Charles Mills schrieb:
> > 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 :-)

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

volatile is a C keyword/qualifier.

>
> >
> >
> > 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.
>

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

ts

1/14/2005 9:49:00 AM

0

>>>>> "P" == Peter Schrammel <peter.schrammel@gmx.de> writes:


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


ts

1/14/2005 10:29:00 AM

0

>>>>> "t" == ts <decoux@moulon.inra.fr> writes:

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




Peter Schrammel

1/14/2005 3:28:00 PM

0

ts schrieb:
>>>>>>"t" == ts <decoux@moulon.inra.fr> writes:
>
>
> 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

Asbjørn Reglund Thorsen

1/15/2005 1:06:00 PM

0