From 0e725b5d9bd534964ae606852453df46d04037ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Klaus=20K=C3=A4mpf?= Date: Thu, 3 Jan 2013 11:56:25 +0100 Subject: [PATCH] Fix Ruby tracking code to use C hash This is a patch to resolve SF bug 2034216 (Github issue #225) The bug is that the tracking code uses a ruby hash and thus may allocate objects (Bignum) while running the GC. This was tolerated in 1.8 but is invalid (raises an exception) in 1.9. The patch uses a C hash (also used by ruby) instead. --- Lib/ruby/rubytracking.swg | 127 ++++++++++++++++++---------------------------- 1 file changed, 49 insertions(+), 78 deletions(-) diff --git a/Lib/ruby/rubytracking.swg b/Lib/ruby/rubytracking.swg index 0a36f4a..d974228 100644 --- a/Lib/ruby/rubytracking.swg +++ b/Lib/ruby/rubytracking.swg @@ -22,19 +22,19 @@ extern "C" { # error sizeof(void*) is not the same as long or long long #endif - -/* Global Ruby hash table to store Trackings from C/C++ +/* Global hash table to store Trackings from C/C++ structs to Ruby Objects. */ -static VALUE swig_ruby_trackings = Qnil; +static st_table* swig_ruby_trackings = NULL; + +VALUE get_swig_trackings_count(ANYARGS) { + return SWIG2NUM(swig_ruby_trackings->num_entries); +} -/* Global variable that stores a reference to the ruby - hash table delete function. */ -static ID swig_ruby_hash_delete; -/* Setup a Ruby hash table to store Trackings */ +/* Setup a hash table to store Trackings */ SWIGRUNTIME void SWIG_RubyInitializeTrackings(void) { - /* Create a ruby hash table to store Trackings from C++ + /* Create a hash table to store Trackings from C++ objects to Ruby objects. */ /* Try to see if some other .so has already created a @@ -43,87 +43,47 @@ 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 trackings_value = Qnil; + /* change the variable name so that we can mix modules + compiled with older SWIG's */ + ID trackings_id = rb_intern( "@__safetrackings__" ); VALUE verbose = rb_gv_get("VERBOSE"); rb_gv_set("VERBOSE", Qfalse); - swig_ruby_trackings = rb_ivar_get( _mSWIG, trackings_id ); + trackings_value = rb_ivar_get( _mSWIG, trackings_id ); rb_gv_set("VERBOSE", verbose); - /* No, it hasn't. Create one ourselves */ - if ( swig_ruby_trackings == Qnil ) - { - swig_ruby_trackings = rb_hash_new(); - rb_ivar_set( _mSWIG, trackings_id, swig_ruby_trackings ); - } - - /* Now store a reference to the hash table delete function - so that we only have to look it up once.*/ - 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 - and then store a reference to it using - a Ruby number object. */ - - /* Convert the Object to a Ruby number */ - return SWIG2NUM(object); -} - -/* Get a Ruby object from a previously stored reference */ -SWIGRUNTIME VALUE SWIG_RubyReferenceToObject(VALUE reference) { - /* The provided Ruby number object is a reference - to the Ruby object we want.*/ + /* The trick here is that we have to store the hash table + pointer in a Ruby variable. We do not want Ruby's GC to + treat this pointer as a Ruby object, so we convert it to + a Ruby numeric value. */ + if (trackings_value == Qnil) { + /* No, it hasn't. Create one ourselves */ + swig_ruby_trackings = st_init_numtable(); + rb_ivar_set( _mSWIG, trackings_id, SWIG2NUM(swig_ruby_trackings) ); + } + else { + swig_ruby_trackings = (st_table*)NUM2SWIG(trackings_value); + } - /* Convert the Ruby number to a Ruby object */ - return NUM2SWIG(reference); + rb_define_virtual_variable("SWIG_TRACKINGS_COUNT", get_swig_trackings_count, NULL); } /* Add a Tracking from a C/C++ struct to a Ruby object */ SWIGRUNTIME void SWIG_RubyAddTracking(void* ptr, VALUE object) { - /* In a Ruby hash table we store the pointer and - the associated Ruby object. The trick here is - that we cannot store the Ruby object directly - if - we do then it cannot be garbage collected. So - 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, object); } /* 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); - - if (value == Qnil) { - /* No object exists - return nil. */ - return Qnil; + VALUE value; + + if (st_lookup(swig_ruby_trackings, (st_data_t)ptr, &value)) { + return value; } else { - /* Convert this value to Ruby object */ - return SWIG_RubyReferenceToObject(value); + return Qnil; } } @@ -132,12 +92,8 @@ 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); + /* Delete the object from the hash table */ + st_delete(swig_ruby_trackings, (st_data_t *)&ptr, NULL); } /* This is a helper method that unlinks a Ruby object from its @@ -147,10 +103,25 @@ SWIGRUNTIME void SWIG_RubyUnlinkObjects(void* ptr) { VALUE object = SWIG_RubyInstanceFor(ptr); if (object != Qnil) { + if (TYPE(object) != T_DATA) + abort(); DATA_PTR(object) = 0; } } +/* This is a helper method that iterates over all the trackings + passing the C++ object pointer and its related Ruby object + to the passed callback function. */ + +/* Proxy method to abstract the internal trackings datatype */ +static int _ruby_internal_iterate_callback(void* ptr, VALUE obj, void(*meth)(void* ptr, VALUE obj)) { + (*meth)(ptr, obj); + return ST_CONTINUE; +} + +SWIGRUNTIME void SWIG_RubyIterateTrackings( void(*meth)(void* ptr, VALUE obj) ) { + st_foreach(swig_ruby_trackings, (int (*)(ANYARGS))&_ruby_internal_iterate_callback, (st_data_t)meth); +} #ifdef __cplusplus } -- 2.4.3