Strange behavior in C-declared global variable? 
Author Message
 Strange behavior in C-declared global variable?

Hi,

I hope someone can help me.

I'm trying to write some C extensions  with
the main objective of speeding up some tasks in Ruby.
I'm relying on the README.ext file as well as
in  Chapter 17 of the 'Prog. Ruby' book from
Thomas and Hunt.

The question is about creating a global variable
in C and see it in Ruby. This is documented
in both those documents, but  has a strange behavior
when implementing. Perhaps I'm missing something.

Here is the script I tried both with Ruby 1.8.0/2003-03-31
version and Ruby 1.6.8. The results are
slightly different. I made a lot of experiments with the code below
in the last few days and looked at the
ruby sources (also the sources in extension modules)
but I didn't see anything that could help me.
The fact that the code runs differently in
1.6.8 and 1.8.0 appears to indicate some kind
of different behavior. The script is:

------------------------------------------------

require 'inline'    # use Inline module

class Fake

inline_c_raw  %q[

    static VALUE newvar(int argc, VALUE *argv, VALUE self) {
      VALUE temp;

      temp = rb_ary_new();
      rb_ary_push(temp, rb_float_new(3.3));
      rb_define_variable("$tempor", &temp);
      rb_global_variable(&temp);
      return Qnil;
    }
]

end

f = Fake.new()
f.newvar()          # calls newvar

puts "****\n"
puts $tempor.class
puts $tempor[0]
puts "****"
puts self.id

#puts global_variables.grep(/tem/)

puts "****\n"
puts $tempor.class
puts $tempor[0]
puts "****"
puts self.id

----------------------------------------------

The C code created by the inline module is

----------------------------------------------

#include "ruby.h"

  static VALUE newvar(int argc, VALUE *argv, VALUE self) {

      VALUE temp;

      temp = rb_ary_new();
      rb_ary_push(temp, rb_float_new(3.3));
      rb_define_variable("$tempor", &temp);
      rb_global_variable(&temp);
      return Qnil;
    }

  VALUE cMod_Fake_newvar;

  void Init_Mod_Fake_newvar() {
    cMod_Fake_newvar = rb_define_module("Mod_Fake_newvar");
    rb_define_method(cMod_Fake_newvar, "newvar", newvar, -1);
  }

----------------------------------------------

The result of running ruby-1.8.0 is the following

----------------------------------------------

****
Fixnum
0
****
537799894
****
z.rb:37: [BUG] Segmentation fault
ruby 1.8.0 (2003-03-31) [i686-linux-gnu]
Aborted

----------------------------------------------

That is, the first time the class and value of $tempor are wrong.
The second time, the examination of $tempor crashes Ruby.
However, the variable is supposedelly global,
and it is 'declared' to  Garbage Collection mechanism
with rb_global_variable(&temp).
The segmentation fault appears to be related with GC.

More strangely, if the line
'#puts global_variables.grep(/tem/)'
is uncommented, there is no segmentation fault
although $tempor is still a Fixnum instead of an Array.
Here is the result:

----------------------------------------------


****
Fixnum
0
****
537799894
$tempor
****
Fixnum
1
****
537799894
----------------------------------------------

But with Ruby1.6.8 (24-12-2002) the segmentation
fault above never occurs, which means it is insensible
to the line "puts global_variables.grep(/tem/)"
being commented or not. However, the Fixnum
instead of Array class of $tempor is still wrong
with ruby 1.6.8...

What am I overlooking here? Are there some extra
'declarations' I shall do to keep the variable global?
(I didn't found much help in the Ruby sources
and in sources of extensions...:-(

Thanks in advance!

Jose Augusto



Mon, 19 Sep 2005 23:08:10 GMT  
 Strange behavior in C-declared global variable?
Quote:

> ------------------------------------------------

> require 'inline'    # use Inline module

> class Fake

> inline_c_raw  %q[

>     static VALUE newvar(int argc, VALUE *argv, VALUE self) {
>       VALUE temp;

>       temp = rb_ary_new();
>       rb_ary_push(temp, rb_float_new(3.3));
>       rb_define_variable("$tempor", &temp);
>       rb_global_variable(&temp);

                           ^
This should have set of your alarm bells. Notice that you give in a
pointer to the VALUE (i.e. the container), which has local scope and is
gone after your C function returns.

Declaring temp as:

  static VALUE temp;

might help, but my C/C++ is rusty.

HTH

--
(\[ Kent Dahl ]/)_    _~_    __[ http://www.stud.ntnu.no/~kentda/ ]___/~
 ))\_student_/((  \__d L b__/  NTNU - graduate engineering - 5. year  )
( \__\_?|?_/__/ ) _)Industrial economics and technological management(
 \____/_?_\____/ (____engineering.discipline_=_Computer::Technology___)



Mon, 19 Sep 2005 22:24:05 GMT  
 Strange behavior in C-declared global variable?

Quote:
>>>>> "J" == =?iso-8859-1?Q?Jos=E9?= Augusto <iso-8859-1> writes:

J>     static VALUE newvar(int argc, VALUE *argv, VALUE self) {
J>       VALUE temp;

J>       temp = rb_ary_new();
J>       rb_ary_push(temp, rb_float_new(3.3));
J>       rb_define_variable("$tempor", &temp);
J>       rb_global_variable(&temp);
J>       return Qnil;
J>     }

 Don't do this rb_define_variable() and rb_global_variable() must be used
 *only* with "global" variables, like for example in

   /* see the declaration of temp0 and temp1 outside the function */
   static VALUE temp0, temp1;

   void Init_tt()
   {
       temp0 = rb_ary_new();
       temp1 = rb_ary_new();
       /*
          this variable can be accessed from ruby and C
       */
       rb_define_variable("$tempor", &temp0);
       /*
          this variable can be accessed only from C
          this is to protect it against the GC
       */
       rb_global_variable(&temp1);
   }

 In your case you want rb_gv_set(), rb_gv_get() (see p. 197 of the pickaxe)

 Something like this (not tested)

    static VALUE newvar(int argc, VALUE *argv, VALUE self) {
      VALUE temp;

      temp = rb_ary_new();
      rb_ary_push(temp, rb_float_new(3.3));
      rb_gv_set("$tempor", temp);
      return Qnil;
    }

Guy Decoux



Mon, 19 Sep 2005 22:29:08 GMT  
 Strange behavior in C-declared global variable?
Hi

Thanks, i'ts really the 'global' versus 'local' problem  (rookie error
;-)...

In fact it is the 'global' scope of the memory where 'temp' is what
counts. By declaring 'static VALUE temp;' or 'VALUE temp;' as global,
outside of the function, the problem disapears.

I examined carefully the code in the Ruby's variable.c file. There are the
following functions:

'rb_global_entry' is the function that creates -- i.e., ** allocs ** -- the
global memory for a new global variable;
'rb_define_variable' --> gets a global_id ID value and calls
'rb_global_entry';
'rb_gv_set' --> calls 'rb_global_entry' and 'rb_gvar_set' to set the
variable.

The function 'rb_global_entry' is the key for creating global vars.
It sees if the variable already exists: if it exists
it returns a pointer to it; if not, allocates memory for a new variable
(structures global_variable
and global_entry) and returns a pointer to global_entry.

The problem with my code, as  Kent Dahl and Guy Decous in another answer
pointed out,
was that I was using   &temp,  ** a pointer to a local value, **
in the function 'rb_define_variable("$tempor",&temp)'.
Of course temp vanishes after exiting the function.

However, 'rb_gv_set("$tempor",temp)' ** uses really temp **, not a pointer
to it, and with it there is no problem,
as Guy Decoux pointed out very well in other reply. So the following code,
with temp a local variable,
works as I intended:
..........
static VALUE newvar(int argc, VALUE *argv, VALUE self) {
       VALUE temp;    // ***** local var

       temp = rb_ary_new();
       rb_ary_push(temp, rb_float_new(3.3));
       rb_gv_set("$tempor", temp);      //  ***** uses temp, not &temp

...........

In fact there is a solution to the problem without using temp at all :-)

static VALUE newvar(int argc, VALUE *argv, VALUE self) {

       rb_ary_push(rb_gv_set("$tempor", rb_ary_new()), rb_float_new(3.3));
...........

which uses only ** global memory ** returned by rb_ary_push, rb_gv_set,
etc...
The key for this  is that 'rb_gv_set("$varname",VALUE val )' returns VALUE
val,
which allows to reuse VALUE val in 'rb_ary_push'.

Thanks again

J. Augusto

Quote:


> > ------------------------------------------------

> > require 'inline'    # use Inline module

> > class Fake

> > inline_c_raw  %q[

> >     static VALUE newvar(int argc, VALUE *argv, VALUE self) {
> >       VALUE temp;

> >       temp = rb_ary_new();
> >       rb_ary_push(temp, rb_float_new(3.3));
> >       rb_define_variable("$tempor", &temp);
> >       rb_global_variable(&temp);
>                            ^
> This should have set of your alarm bells. Notice that you give in a
> pointer to the VALUE (i.e. the container), which has local scope and is
> gone after your C function returns.

> Declaring temp as:

>   static VALUE temp;

> might help, but my C/C++ is rusty.

> HTH

> --
> (\[ Kent Dahl ]/)_    _~_    __[ http://www.stud.ntnu.no/~kentda/ ]___/~
>  ))\_student_/((  \__d L b__/  NTNU - graduate engineering - 5. year  )
> ( \__\_?|?_/__/ ) _)Industrial economics and technological management(
>  \____/_?_\____/ (____engineering.discipline_=_Computer::Technology___)



Tue, 20 Sep 2005 23:44:46 GMT  
 Strange behavior in C-declared global variable?
Hi

Thanks very much for your reply. You are right and declaring temp as static
solves the problem, as well as using rb_iv_set. The code
you suggested below works perfectly!

I examined the interrelation of rb_gv_set and rb_define_variable
in the  Ruby code (file variable.c) and posted my conclusions in other post
(replying to Kent Dahls' answer), so I dont repeat them here.

Thanks

J. Augusto

Quote:

> >>>>> "J" == =?iso-8859-1?Q?Jos=E9?= Augusto <iso-8859-1> writes:

> J>     static VALUE newvar(int argc, VALUE *argv, VALUE self) {
> J>       VALUE temp;

> J>       temp = rb_ary_new();
> J>       rb_ary_push(temp, rb_float_new(3.3));
> J>       rb_define_variable("$tempor", &temp);
> J>       rb_global_variable(&temp);
> J>       return Qnil;
> J>     }

>  Don't do this rb_define_variable() and rb_global_variable() must be used
>  *only* with "global" variables, like for example in

>    /* see the declaration of temp0 and temp1 outside the function */
>    static VALUE temp0, temp1;

>    void Init_tt()
>    {
>        temp0 = rb_ary_new();
>        temp1 = rb_ary_new();
>        /*
>           this variable can be accessed from ruby and C
>        */
>        rb_define_variable("$tempor", &temp0);
>        /*
>           this variable can be accessed only from C
>           this is to protect it against the GC
>        */
>        rb_global_variable(&temp1);
>    }

>  In your case you want rb_gv_set(), rb_gv_get() (see p. 197 of the pickaxe)

>  Something like this (not tested)

>     static VALUE newvar(int argc, VALUE *argv, VALUE self) {
>       VALUE temp;

>       temp = rb_ary_new();
>       rb_ary_push(temp, rb_float_new(3.3));
>       rb_gv_set("$tempor", temp);
>       return Qnil;
>     }

> Guy Decoux



Tue, 20 Sep 2005 23:50:58 GMT  
 
 [ 5 post ] 

 Relevant Pages 

1. Where declaring global variables when using exe + dll's with different data-dictionaries

2. ?Accessing Global variables (declared in C) in ASM subroutine

3. How do you declare global variables?

4. How to declare a global variable

5. declaring global variables

6. Declaring global variable

7. Strange ACL printer variable behavior

8. CW2001.01 Very strange warnings with global variables

9. Class variables / global variables / Init variables

10. Automatically declaring a global object

11. Declaring global data for subapplications

12. Dll Declared Global Class

 

 
Powered by phpBB® Forum Software