Storing c pointers in ruby hash

Hi all,
I’m been banging my head on the keyboard the whole day trying to track
a bug.
I have a embedded ruby (branch 1_9_1, checked out today) running on a
Objective-C project (if anyone’s interested, it’s this project:
GitHub - rolandoam/shinycocos: ruby bindings for cocos2d-iphone (a.k.a.: i want to program my iphone game in ruby))
.
The idea is to keep a reference to the ruby counterpart of the Obj-C
object. Right now, I’m using a “global” hash table, defined as this:

VALUE sc_object_hash = rb_hash_new();

The variable has been registered to the GC as suggested in the
README.EXT:

rb_global_variable(&sc_object_hash);

An objc object is associated to a new ruby object (the ruby version of
the objc class) this way:

node = [[SomeClass alloc] init];
VALUE rb_obj = common_init(klass, nil, node, argc, argv, YES);
common_link(node, rb_obj);

common_init is a function defined as follows:

VALUE common_init(VALUE klass, cocos_holder **ret_ptr, id object, int
argc, VALUE *argv, BOOL release_on_free) {
VALUE obj;
cocos_holder *ptr;
if (release_on_free)
obj = Data_Make_Struct(klass, cocos_holder, 0, common_free, ptr);
else
obj = Data_Make_Struct(klass, cocos_holder, 0,
common_free_no_release, ptr);
ptr->_obj = object;
rb_obj_call_init(obj, argc, argv);
if (ret_ptr != nil)
*ret_ptr = ptr;
return obj;
}

and common_link is an inline function:

static inline void common_link(id obj1, VALUE obj2) {
rb_hash_aset(sc_object_hash, INT2FIX((long)obj1), obj2);
}

Some times, the objc framework will call a method in the objc object.
When this happens, I need to call the proper ruby method in the
corresponding ruby object:

  • (void)someMethodCalledByTheFramework {
    VALUE rb_obj = common_link_ref(self);
    rb_funcall(rb_obj, id_cb_on_enter, 0, 0);
    }

common_link_ref is the counterpart of common_link (basically calls
rb_hash_aref on the same hash)
The thing is, that the rb_obj is crashing the ruby interpreter. If I
try to call any method on it (even inspect) I get a bad access error.
It was working with a ruby 1.8 version, but now that I’ve ruby 1.9, it
refuses to work… Any idea of what I could be doing wrong here?
Is there a better way to link a ruby object with a c pointer? I need
to get the associated ruby object to a given objc object. Not the
other way around (which is easy using Data_Get_Struct)… that’s why
I’m using a hash table.

thanks!

also, if I try to inspect the contents of the hash, I also get a core
dump.

On May 6, 2009, at 4:59 PM, Rolando A. wrote:

common_link(node, rb_obj);
obj = Data_Make_Struct(klass, cocos_holder, 0,
static inline void common_link(id obj1, VALUE obj2) {
}
other way around (which is easy using Data_Get_Struct)… that’s why
I’m using a hash table.

thanks,

Rolando A. wrote:

I’m been banging my head on the keyboard the whole day trying to
track a bug.

You probably should post more information about what happens when the
bug hits: eg is it a rb_bug, and if so, with what message? Can you
provide a gdb backtrace of the C call stack?

The idea is to keep a reference to the ruby counterpart of the Obj-C
object. Right now, I’m using a “global” hash table, defined as this:

The code looks ok from a casual look, though I’m not sure INT2FIX is the
right conversion macro to use. SWIG does this to create a conversion
macro (SWIG2NUM) for making Ruby hash keys from pointers.

/* Ruby 1.8 actually assumes the first case. */
#if SIZEOF_VOIDP == SIZEOF_LONG

define SWIG2NUM(v) LONG2NUM((unsigned long)v)

define NUM2SWIG(x) (unsigned long)NUM2LONG(x)

#elif SIZEOF_VOIDP == SIZEOF_LONG_LONG

define SWIG2NUM(v) LL2NUM((unsigned long long)v)

define NUM2SWIG(x) (unsigned long long)NUM2LL(x)

#else

error sizeof(void*) is not the same as long or long long

#endif

The thing is, that the rb_obj is crashing the ruby interpreter. If I
try to call any method on it (even inspect) I get a bad access
error. It was working with a ruby 1.8 version, but now that I’ve
ruby 1.9, it refuses to work… Any idea of what I could be doing
wrong here?

If above doesn’t help, what version of 1.8 were you using previously? If
< 1.8.7 then you might see if the bug is “object allocation during
garbage collection”. The size of pointers on Mac OS X mean that when
they are converted to ruby Numerics, they may be BigNums. These are not
immediate objects and involve allocation; if you happen to do this
during GC it’s a mistake. Ruby 1.8.6 and earlier were indulgent to this
failing, but 1.8.7 and 1.9.1 are not.

You could test this by placing GC.disable at the very start of your
script and seeing if the crash still happens.

Is there a better way to link a ruby object with a c pointer? I need
to get the associated ruby object to a given objc object.

Do you have an alternate hash map implementation available, eg from ObjC
standard libraries? Can you use this instead to create a straight
pointer->VALUE map? I would try this as using Ruby Hash adds unnecessary
complexity involved with converting the pointer keys to VALUE, and GC.

Several of the things you appear to be trying to do (have a persistent
ObjC -> Ruby object link; forward method calls from ObjC to Ruby) are
similar to what SWIG provides for C++ and you might find it useful to
look at the implementation of “object tracking” and
“directors”/“cross-language polymorphism” there.

The former uses a hash, the latter uses a C++ class which multiply
inherits from the wrapped C++ class and a simple C++ class called
“Director” which has the VALUE as member, and forwards method calls to
rb_funcall on that VALUE.

hth
alex

On May 7, 2009, at 7:15 AM, Alex F. wrote:

Rolando A. wrote:

I’m been banging my head on the keyboard the whole day trying to
track a bug.

You probably should post more information about what happens when
the bug hits: eg is it a rb_bug, and if so, with what message? Can
you provide a gdb backtrace of the C call stack?

Here’s the backtrack when trying to use the Data_Get_Struct() macro on
the object back from the hash table:

#0 0x000a546c in st_lookup (table=0xdaa29, key=9864,
value=0xbfffe28c) at /Users/rolando/Documents/GFF/ShinyCocos/ruby/st.c:
289
#1 0x0010475e in search_method (klass=92302960, id=9864, klassp=0x0)
at vm_method.c:229
#2 0x001047a8 in rb_get_method_body (klass=92302960, id=9864,
idp=0xbfffe334) at vm_method.c:256
#3 0x00106ff9 in rb_call0 (klass=92302960, recv=92302800, mid=9864,
argc=0, argv=0x0, scope=1, self=6) at vm_eval.c:205
#4 0x00106ea7 in rb_call (klass=92302960, recv=92302800, mid=9864,
argc=0, argv=0x0, scope=1) at vm_eval.c:255
#5 0x001072cf in rb_funcall (recv=92302800, mid=9864, n=0) at
vm_eval.c:427
#6 0x000d10e2 in -[CocosNode(SC_Extension) rb_on_enter]
(self=0x5778540, _cmd=0x1a75dc) at /Users/rolando/Documents/GFF/
ShinyCocos/Integration/SC_CocosNode.m:72
#7 0x00174a11 in -[CocosNode onEnter] (self=0x5778330, _cmd=0x19ca9f)
at /Users/rolando/Documents/GFF/ShinyCocos/cocos2d-iphone/cocos2d/
CocosNode.m:529
#8 0x000d10ac in -[CocosNode(SC_Extension) rb_on_enter]
(self=0x5778330, _cmd=0x1a75dc) at /Users/rolando/Documents/GFF/
ShinyCocos/Integration/SC_CocosNode.m:69
#9 0x00174a11 in -[CocosNode onEnter] (self=0x5775e90, _cmd=0x19ca9f)
at /Users/rolando/Documents/GFF/ShinyCocos/cocos2d-iphone/cocos2d/
CocosNode.m:529
#10 0x000d10ac in -[CocosNode(SC_Extension) rb_on_enter]
(self=0x5775e90, _cmd=0x1a75dc) at /Users/rolando/Documents/GFF/
ShinyCocos/Integration/SC_CocosNode.m:69
#11 0x00174a11 in -[CocosNode onEnter] (self=0x5775e30, _cmd=0x19ca9f)
at /Users/rolando/Documents/GFF/ShinyCocos/cocos2d-iphone/cocos2d/
CocosNode.m:529
#12 0x000d10ac in -[CocosNode(SC_Extension) rb_on_enter]
(self=0x5775e30, _cmd=0x1a75dc) at /Users/rolando/Documents/GFF/
ShinyCocos/Integration/SC_CocosNode.m:69
#13 0x00179386 in -[Director setNextScene] (self=0x1070fd0,
_cmd=0x1a7882) at /Users/rolando/Documents/GFF/ShinyCocos/cocos2d-
iphone/cocos2d/Director.m:649
#14 0x00177a13 in -[Director mainLoop] (self=0x1070fd0, _cmd=0x1a76ff)
at /Users/rolando/Documents/GFF/ShinyCocos/cocos2d-iphone/cocos2d/
Director.m:185
#15 0x94505e23 in __NSFireTimer ()
#16 0x9213cb25 in CFRunLoopRunSpecific ()
#17 0x9213ccd8 in CFRunLoopRunInMode ()
#18 0x31566600 in GSEventRunModal ()
#19 0x315666c5 in GSEventRun ()
#20 0x30a4eca0 in -[UIApplication _run] ()
#21 0x30a5a09c in UIApplicationMain ()
#22 0x0000200a in main (argc=1, argv=0xbfffeee4) at /Users/rolando/
Documents/GFF/ShinyCocos/_DC/main.m:14

define SWIG2NUM(v) LL2NUM((unsigned long long)v)

define NUM2SWIG(x) (unsigned long long)NUM2LL(x)

#else

error sizeof(void*) is not the same as long or long long

#endif

I thought about this. I tried using (ruby) string keys just to make
sure it wasn’t that sort of problem. Same issue. I even went further
and removed the hash table, using an instance variable in the objc
class that will be linked to a ruby class (I wanted to avoid this). It
crashed in the same way.

If above doesn’t help, what version of 1.8 were you using
previously? If < 1.8.7 then you might see if the bug is “object
allocation during garbage collection”. The size of pointers on Mac
OS X mean that when they are converted to ruby Numerics, they may be
BigNums. These are not immediate objects and involve allocation; if
you happen to do this during GC it’s a mistake. Ruby 1.8.6 and
earlier were indulgent to this failing, but 1.8.7 and 1.9.1 are not.

It was 1.8.8: the stable snapshot that’s on the ruby page, which I
mistakenly confused with a stable snapshot of the 1.9.1 series, I
think that link is a bit outdated.

You could test this by placing GC.disable at the very start of your
script and seeing if the crash still happens.

I’ll try that.

Do you have an alternate hash map implementation available, eg from
ObjC standard libraries? Can you use this instead to create a
straight pointer->VALUE map? I would try this as using Ruby Hash
adds unnecessary complexity involved with converting the pointer
keys to VALUE, and GC.

I’ll try NSDictionary, but as I got the same error using the raw
pointer… I’ll give it a shot though.

Several of the things you appear to be trying to do (have a
persistent ObjC -> Ruby object link; forward method calls from ObjC
to Ruby) are similar to what SWIG provides for C++ and you might
find it useful to look at the implementation of “object tracking”
and “directors”/“cross-language polymorphism” there.

The former uses a hash, the latter uses a C++ class which multiply
inherits from the wrapped C++ class and a simple C++ class called
“Director” which has the VALUE as member, and forwards method calls
to rb_funcall on that VALUE.

thanks! I’ll take a look at that code.

hth
alex

regards,

No luck yet with this (still banging my head with the keyboard).
After looking at swig source code and reading this:

<SWIG / Bugs / #939 [ruby] generated tracking code errors with Ruby 1.8.7 / 1.9

I realized that using a ruby hash was a bad idea. Although I had
tested using a simple pointer, I changed the approach using a
NSDictionary. I also added some debug output, and the VALUE I get back
from the hash is the same, but If I call any function on that object,
I get a crash:

#0 0x000a56c6 in st_lookup (table=0x0, key=760, value=0xbfffe20c) at /
Users/rolando/Documents/GFF/ShinyCocos/ruby/st.c:286
#1 0x00105702 in search_method (klass=92295000, id=760, klassp=0x0)
at vm_method.c:229
#2 0x0010574c in rb_get_method_body (klass=92295000, id=760,
idp=0xbfffe2b4) at vm_method.c:256
#3 0x00107f9d in rb_call0 (klass=92295000, recv=92302820, mid=760,
argc=0, argv=0x0, scope=1, self=6) at vm_eval.c:205
#4 0x00107e4b in rb_call (klass=92295000, recv=92302820, mid=760,
argc=0, argv=0x0, scope=1) at vm_eval.c:255
#5 0x00108273 in rb_funcall (recv=92302820, mid=760, n=0) at
vm_eval.c:427
#6 0x000d1811 in sc_ruby_instance_for (hash=0x10622e0,
obj1=0x5778810) at SC_common.h:86

… should I fill a bug report? :-S
I’ll try to put a simple demo file to simplify the example (although
the latest code that reflects this is in
<GitHub - rolandoam/shinycocos: ruby bindings for cocos2d-iphone (a.k.a.: i want to program my iphone game in ruby)

, you might need iPhone SDK to test it though).

Thanks for any tip!

On May 7, 2009, at 7:15 AM, Alex F. wrote:

If above doesn’t help, what version of 1.8 were you using
previously? If < 1.8.7 then you might see if the bug is “object
allocation during garbage collection”. The size of pointers on Mac
OS X mean that when they are converted to ruby Numerics, they may be
BigNums. These are not immediate objects and involve allocation; if
you happen to do this during GC it’s a mistake. Ruby 1.8.6 and
earlier were indulgent to this failing, but 1.8.7 and 1.9.1 are not.

You could test this by placing GC.disable at the very start of your
script and seeing if the crash still happens.

disabling the GC, lend me to rb_bug:

:2: [BUG] Segmentation fault
ruby 1.9.1p0 (2009-05-04 revision 23343) [i386-darwin9.6.0]

#0 0x0010dc54 in control_frame_dump (th=0x10248e0, cfp=0xfaefd0) at /
Users/rolando/Documents/GFF/ShinyCocos/ruby/vm_dump.c:108
#1 0x0010e016 in rb_vmdebug_stack_dump_raw (th=0x10248e0,
cfp=0xfaefd0) at /Users/rolando/Documents/GFF/ShinyCocos/ruby/
vm_dump.c:176
#2 0x0010ea2e in rb_vm_bugreport () at /Users/rolando/Documents/GFF/
ShinyCocos/ruby/vm_dump.c:575
#3 0x00024d05 in report_bug (file=0x640014 “\034\001”, line=2,
fmt=0x19b143 “Segmentation fault”, args=0x7e8cd4 “\213E\b\211”) at /
Users/rolando/Documents/GFF/ShinyCocos/ruby/error.c:215
#4 0x00024d6f in rb_bug (fmt=0x19b143 “Segmentation fault”) at /Users/
rolando/Documents/GFF/ShinyCocos/ruby/error.c:230
#5 0x0009d7bc in sigsegv (sig=11, info=0x7e8fa0, ctx=0x7e8fe0) at /
Users/rolando/Documents/GFF/ShinyCocos/ruby/signal.c:605
#6
#7 0x000a546c in st_lookup (table=0xdaa29, key=9864,
value=0xbfffe28c) at /Users/rolando/Documents/GFF/ShinyCocos/ruby/st.c:
289
#8 0x0010475e in search_method (klass=92302780, id=9864, klassp=0x0)
at vm_method.c:229
#9 0x001047a8 in rb_get_method_body (klass=92302780, id=9864,
idp=0xbfffe334) at vm_method.c:256
#10 0x00106ff9 in rb_call0 (klass=92302780, recv=92302620, mid=9864,
argc=0, argv=0x0, scope=1, self=6) at vm_eval.c:205
#11 0x00106ea7 in rb_call (klass=92302780, recv=92302620, mid=9864,
argc=0, argv=0x0, scope=1) at vm_eval.c:255
#12 0x001072cf in rb_funcall (recv=92302620, mid=9864, n=0) at
vm_eval.c:427
#13 0x000d10e2 in -[CocosNode(SC_Extension) rb_on_enter]
(self=0x5778580, _cmd=0x1a75dc) at /Users/rolando/Documents/GFF/
ShinyCocos/Integration/SC_CocosNode.m:72
#14 0x00174a11 in -[CocosNode onEnter] (self=0x5778370, _cmd=0x19ca9f)
at /Users/rolando/Documents/GFF/ShinyCocos/cocos2d-iphone/cocos2d/
CocosNode.m:529
#15 0x000d10ac in -[CocosNode(SC_Extension) rb_on_enter]
(self=0x5778370, _cmd=0x1a75dc) at /Users/rolando/Documents/GFF/
ShinyCocos/Integration/SC_CocosNode.m:69
#16 0x00174a11 in -[CocosNode onEnter] (self=0x5775ed0, _cmd=0x19ca9f)
at /Users/rolando/Documents/GFF/ShinyCocos/cocos2d-iphone/cocos2d/
CocosNode.m:529
#17 0x000d10ac in -[CocosNode(SC_Extension) rb_on_enter]
(self=0x5775ed0, _cmd=0x1a75dc) at /Users/rolando/Documents/GFF/
ShinyCocos/Integration/SC_CocosNode.m:69
#18 0x00174a11 in -[CocosNode onEnter] (self=0x5775e70, _cmd=0x19ca9f)
at /Users/rolando/Documents/GFF/ShinyCocos/cocos2d-iphone/cocos2d/
CocosNode.m:529
#19 0x000d10ac in -[CocosNode(SC_Extension) rb_on_enter]
(self=0x5775e70, _cmd=0x1a75dc) at /Users/rolando/Documents/GFF/
ShinyCocos/Integration/SC_CocosNode.m:69
#20 0x00179386 in -[Director setNextScene] (self=0x1070fd0,
_cmd=0x1a7882) at /Users/rolando/Documents/GFF/ShinyCocos/cocos2d-
iphone/cocos2d/Director.m:649
#21 0x00177a13 in -[Director mainLoop] (self=0x1070fd0, _cmd=0x1a76ff)
at /Users/rolando/Documents/GFF/ShinyCocos/cocos2d-iphone/cocos2d/
Director.m:185
#22 0x94505e23 in __NSFireTimer ()
#23 0x9213cb25 in CFRunLoopRunSpecific ()
#24 0x9213ccd8 in CFRunLoopRunInMode ()
#25 0x31566600 in GSEventRunModal ()
#26 0x315666c5 in GSEventRun ()
#27 0x30a4eca0 in -[UIApplication _run] ()
#28 0x30a5a09c in UIApplicationMain ()
#29 0x0000200a in main (argc=1, argv=0xbfffeee4) at /Users/rolando/
Documents/GFF/ShinyCocos/_DC/main.m:14

Will check a few things and try again.
Btw, you were totally right, the way swig tracks objects is almost the
same thing I’m trying to do. I’ll take a closer look to the code.

cheers,