On Sep 29, 2004, at 4:20 PM, Daniel Berger wrote:
>
>> Also, ULL2NUM() (and all the 2NUM() macros) can call rb_gc() if the
>> argument is out of Fixnum range (a Bignum will be created - possibly
>> calling rb_gc() in the process).  This could cause the key argument to
>> be collected by the gc (I think).
>
> I'd like to find out for certain.  I'll have to dig.  However, I
> didn't tinker with these, and things seem to be working fine.
It is probably a rare case where the arguments of ULL2NUM() and friends 
are out of Fixnum range, but if they are bignew_1() in bignum.c will be 
called which calls ALLOC_N() which can call rb_gc()... so a number of 
things will have to happen before *2NUM() starts causing problems.
If you browse the Ruby sources much I have some stuff I use to run 
Doxygen on the Ruby sources posted my webpage:
http://cmills.freeshell.org/
...
Another little optimization I though of right after I sent that email 
is freezing String keys before you call rb_hash_aset...
So to set your hash elements safely you could do:
void
hash_add_pair(VALUE hash, const char *key, volatile VALUE val)
{
	rb_hash_aset(hash, rb_str_freeze(rb_str_new2(key)), val);
}

Saves some memory allocation and takes away the ULL2NUM() worries.

>
> Well, between you and Guy, I think it's solved - THANKS!  Now, I just
> need the "Dummy's Guide to 'Volatile' in Ruby Extensions". :)

Glad to hear you got everything working.  Funny you mentioned the 
"Dummy's Guide to 'Volatile' in Ruby Extensions" because after I sent 
that email to you I started thinking about what are good rules for when 
to use volatile and when not to.  Here is my understanding... somebody 
please chime in if this is wrong:

- Objects passed as parameters don't need to be placed in volatile 
storage.
Unless your hacking the interpreter functions don't have to make sure 
their parameters are safe from the GC (not the functions 
responcibility).

- If you create an object in your function you need to make sure it is 
in volatile storage, unless you are not going to call any functions 
which may call the GC after the object is created.

- All functions which allocate (or reallocate) memory, rb_file_open(), 
and probably a few others can call the GC.

- If your placing object in a structure (like Array or Hash) which is 
protected from the GC then those objects are protected / get marked.

Here are a couple contrived and untested examples:

VALUE
my_name_is(const char *name)
{
	return rb_str_new2(name);
}
##
Obviously you don't need to worry about wether or not the Ruby String 
created in the above function is going to be collected by the GC.  
However,
##
VALUE
my_fullname_is(const char *first, const char *last)
{
	VALUE fullname = rb_str_new2(first);
	rb_str_append(fullname, rb_str_new2(" "));
	return rb_str_append(fullname, rb_str_new2(last));
}
##
the variable fullname above should be volatile.  Is this true if we use 
rb_str_cat2() instead of append?
##
void
push_name_pair(VALUE ary, const char *first, const char *last)
{
	VALUE pair = rb_ary_new2(2);
	rb_ary_push(ary, pair); /* now pair is safe */

	/* pair will mark the strings created from first and last */
	rb_ary_push(pair, rb_str_new2(first));
	rb_ary_push(pair, rb_str_new2(last));
}
##
As far as I can tell everything in the above function is safe from the 
gc.

-Charlie