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


[jasa@localhost rb]$ ruby z.rb


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:


[jasa@localhost rb]$ ruby z.rb


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…:frowning:

Thanks in advance!

Jose Augusto

    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;
    }

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

José Augusto wrote:


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)

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 :slight_smile:

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

Kent Dahl wrote:

···

José Augusto wrote:


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)

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

ts wrote:

···
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;
}

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