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

ruby (61)

SWIG's generated tracking code is not compatible with the latest ruby versions : 1.8.7 and 1.9

Specifically, during ruby's garbage collection phase, the function SWIG_RubyRemoveTracking may be called, which in turn calls SWIG_RubyPtrToReference.

This converts a void* (ptr to the C++ object) to a ruby numeric corresponding to the pointer address, which is then used as a ruby hash key. The pointer address may convert into a Ruby BigNum, which is a new object allocation. As of Ruby 1.8.7 / Ruby 1.9 object allocation during GC phase is a bug and will crash out with an error message.

For the time being we (wxRuby) have worked round this be re-implementing the tracking code using a HashMap class from wxWidgets, but this isn't a general solution. This problem will affect any SWIG/Ruby project that uses the tracking code.


  • Olly Betts

    Olly Betts - 2008-10-01
    • summary: SWIG's generated tracking code errors with Ruby 1.8.7 / 1.9 --> [ruby] generated tracking code errors with Ruby 1.8.7 / 1.9
  • Kevin Burge

    Kevin Burge - 2008-10-23

    If you look at the Ruby repository when the "object allocation during garbage collection phase" patch was applied:

    svn diff -r17139:17140 http://svn.ruby-lang.org/repos/ruby/branches/ruby_1_8_6

    You'll see they had the same hash-tracking problem for DL. I just modified my own swig generated code with the following patch, and it seems to have resolved the Fixnum problem for the tracking swig object pointers:

    diff --git a/xtt/swig/ruby/xtt.c b/xtt/swig/ruby/xtt.c
    index f64e987..ded5f89 100644
    --- a/xtt/swig/ruby/xtt.c
    +++ b/xtt/swig/ruby/xtt.c
    @@ -802,6 +802,7 @@ SWIG_UnpackDataName(const char *c, void *ptr, size_t sz, const char *name) {

    #include <ruby.h>
    +#include <st.h>

    /* Remove global macros defined in Ruby's win32.h */
    #ifdef write
    @@ -1095,7 +1096,7 @@ extern "C" {
    /* Global Ruby hash table to store Trackings from C/C++
    structs to Ruby Objects.
    -static VALUE swig_ruby_trackings = Qnil;
    +static st_table* swig_ruby_trackings;

    /* Global variable that stores a reference to the ruby
    hash table delete function. */
    @@ -1112,17 +1113,14 @@ SWIGRUNTIME void SWIG_RubyInitializeTrackings(void) {
    This is done to allow multiple DSOs to share the same
    tracking table.
    - ID trackings_id = rb_intern( "@__trackings__" );
    VALUE verbose = rb_gv_get("VERBOSE");
    rb_gv_set("VERBOSE", Qfalse);
    - swig_ruby_trackings = rb_ivar_get( _mSWIG, trackings_id );
    rb_gv_set("VERBOSE", verbose);

    /* No, it hasn't. Create one ourselves */
    - if ( swig_ruby_trackings == Qnil )
    + if ( !swig_ruby_trackings )
    - swig_ruby_trackings = rb_hash_new();
    - rb_ivar_set( _mSWIG, trackings_id, swig_ruby_trackings );
    + swig_ruby_trackings = st_init_numtable();

    /* Now store a reference to the hash table delete function
    @@ -1130,16 +1128,6 @@ SWIGRUNTIME void SWIG_RubyInitializeTrackings(void) {
    swig_ruby_hash_delete = rb_intern("delete");

    -/* Get a Ruby number to reference a pointer */
    -SWIGRUNTIME VALUE SWIG_RubyPtrToReference(void* ptr) {
    - /* We cast the pointer to an unsigned long
    - and then store a reference to it using
    - a Ruby number object. */
    - /* Convert the pointer to a Ruby number */
    - return SWIG2NUM(ptr);
    /* Get a Ruby number to reference an object */
    SWIGRUNTIME VALUE SWIG_RubyObjectToReference(VALUE object) {
    /* We cast the object to an unsigned long
    @@ -1168,23 +1156,21 @@ SWIGRUNTIME void SWIG_RubyAddTracking(void* ptr, VALUE object) {
    instead we typecast it as a unsigned long and
    convert it to a Ruby number object.*/

    - /* Get a reference to the pointer as a Ruby number */
    - VALUE key = SWIG_RubyPtrToReference(ptr);
    /* Get a reference to the Ruby object as a Ruby number */
    VALUE value = SWIG_RubyObjectToReference(object);

    /* Store the mapping to the global hash table. */
    - rb_hash_aset(swig_ruby_trackings, key, value);
    + st_insert(swig_ruby_trackings, (st_data_t)ptr, value);

    /* Get the Ruby object that owns the specified C/C++ struct */
    SWIGRUNTIME VALUE SWIG_RubyInstanceFor(void* ptr) {
    - /* Get a reference to the pointer as a Ruby number */
    - VALUE key = SWIG_RubyPtrToReference(ptr);
    /* Now lookup the value stored in the global hash table */
    - VALUE value = rb_hash_aref(swig_ruby_trackings, key);
    + VALUE value;
    + if (!st_lookup(swig_ruby_trackings, (st_data_t)ptr, &value)) {
    + return Qnil;
    + }

    if (value == Qnil) {
    /* No object exists - return nil. */
    @@ -1201,12 +1187,9 @@ SWIGRUNTIME VALUE SWIG_RubyInstanceFor(void* ptr) {
    since the same memory address may be reused later to create
    a new object. */
    SWIGRUNTIME void SWIG_RubyRemoveTracking(void* ptr) {
    - /* Get a reference to the pointer as a Ruby number */
    - VALUE key = SWIG_RubyPtrToReference(ptr);
    /* Delete the object from the hash table by calling Ruby's
    do this we need to call the Hash.delete method.*/
    - rb_funcall(swig_ruby_trackings, swig_ruby_hash_delete, 1, key);
    + st_delete(swig_ruby_trackings, (st_data_t *)&ptr, NULL);

    /* This is a helper method that unlinks a Ruby object from its
    @@ -8930,6 +8913,7 @@ SWIGEXPORT void Init_xtt_api(void) { /* MANUAL */

    + swig_ruby_trackings = NULL;
    rb_define_const(mCore, "XTT_EI_OKAY", SWIG_From_int((int)(XTT_EI_OKAY)));
    rb_define_const(mCore, "XTT_EI_ERR_in", SWIG_From_int((int)(XTT_EI_ERR_in)));

  • Tobias Grimm

    Tobias Grimm - 2009-02-11

    Just stumbled across this bug as well. I borrowed the wxRuby workaround (thanks guys!) with a std::map, but this should really be fixed in SWIG.

  • Tobias Grimm

    Tobias Grimm - 2009-02-11

    Just stumbled across this bug as well. I borrowed the wxRuby workaround (thanks guys!) with a std::map, but this should really be fixed in SWIG.

  • cfis

    cfis - 2009-03-05

    I think the best solution here is to use ruby's C level hash table - st_table. When I originally implemented this code I didn't realize that existed. That would eliminate the call back into Ruby....

  • Comment has been marked as spam. 

    You can see all pending comments posted by this user  here


    Anonymous - 2012-10-27

    I have reimplemented rubytrackings.swg with the C ruby hash. I can't add the patch file to this ticket, please tell me how to help so that this ticket may be closed.

    Last edit: Anonymous 2013-09-08
  • William Fulton

    William Fulton - 2012-12-10
    • assigned_to: gga73 --> kkaempf
  • Klaus Kämpf

    Klaus Kämpf - 2012-12-19

    @phi: I've seen you patch below 'patches', thanks. Will be applied shortly (after swig move to github is finished)

  • Klaus Kämpf

    Klaus Kämpf - 2013-01-03

    This needs a testcase

  • Pascal Hurni

    Pascal Hurni - 2013-01-09

    This will be a tricky test to set up. We have to create a C++ object that is located in memory very high to force the tracking code to convert it to a BigNum.

    I investigated a bit, and found that we may use boost to allocate a shared memory segment at a fixed location (so that we can force to have a high address).

    I don't have any skills with boost, can someone here take this?

  • Pascal Hurni

    Pascal Hurni - 2013-02-11

    Ok, I managed to allocate an object at a fixed address with boost. The problem is that asking for a high address raises an exception (at least under CentOS 6.3 x64).

    We know for sure the current tracking code is buggy. I propose to commit the new one without new test cases, it wont' hurt and we'll be able to go further.

  • William Fulton

    William Fulton - 2015-09-13
    • status: open --> closed
    • Group: -->

Log in to post a comment.