# SWIG Ruby Memory Management

**URL:** <https://rubytalk.org/t/swig-ruby-memory-management/20265>\
**Category:** ruby-talk\
**Created:** [18 August 2005 23:53 UTC](https://rubytalk.org/t/swig-ruby-memory-management/20265 "2005-08-18T23:53:12Z")\
**Posts on this page:** 2\
**Page:** 1

<div class="post-metadata">

**Author:** ![Charlie\_Savage](https://avatars.discourse-cdn.com/v4/letter/c/57b2e6/32.png) [@Charlie\_Savage](https://rubytalk.org/u/Charlie_Savage)\
**Post date:** [18 August 2005 23:53 UTC](https://rubytalk.org/t/swig-ruby-memory-management/20265/1 "2005-08-18T23:53:12Z")

</div>

Hi everyone,

I've recently gotten up to speed with SWIG as I try to create Ruby  
bindings for the GDAL open source project. SWIG Ruby supports seems  
pretty good with one exception - memory management. I have run into  
three issues:

\* There is no support for telling SWIG that ownership of an object has  
changed

\* There is no built in infrastructure for making it easy to implement a  
mark function

\* Multiple Ruby objects can wrap the same C++ object / C struct.

I describe these issues in more detail in these two posts:

[http://mailman.cs.uchicago.edu/pipermail/swig-dev/2005-August/014991.html](http://mailman.cs.uchicago.edu/pipermail/swig-dev/2005-August/014991.html)  
[http://mailman.cs.uchicago.edu/pipermail/swig-dev/2005-August/014992.html](http://mailman.cs.uchicago.edu/pipermail/swig-dev/2005-August/014992.html)

Since these seem to be problems that many people run into when writing  
Ruby extensions with SWIG, I have spent some time implementing generic  
solutions for them and would like to run them by the list for  
comment/feedback/suggestions.

API

> **···**
>
> ---------  
> The basic api is:
> 
> /\* Add a mapping from a C/C++ struct to a Ruby object \*/  
> static void SWIG\_RubyAddMapping(void\* ptr, VALUE object);
> 
> /\* Get the Ruby object that owns the specified C/C++ struct \*/  
> static VALUE SWIG\_RubyInstanceFor(void\* ptr);
> 
> /\* Remove a mapping from a C/C++ struct to a Ruby object \*/  
> static void SWIG\_RubyRemoveMapping(void\* ptr);
> 
> The SWIG wrapper code takes care of calling RubyAddMapping and  
> RubyRemoveMapping when Ruby objects in extensions are created/deleted.  
> Thus the extension programmer generally only has to call  
> SWIG\_RubyInstanceFor when implementing a mark function (although in some  
> cases you may have to call SWIG\_RubyRemoveMapping if your are manually  
> destroying objects on the C++ side).
> 
> Implementation  
> ----------------  
> To solve issues #2 and #3 requires keeping track of mappings between  
> C/C++ structs and Ruby objects. Reading through various newsgroups I  
> have seen two general approaches - have a hash table that stores these  
> mappings or add an field to each C/C++ struct to store a reference to a  
> Ruby object.
> 
> As can be seen in the code below, I have gone down the hashtable route  
> with a slight twist - I use a Ruby hash table from C to store these  
> mappings. I did this instead of using a C++ map since I wanted to the  
> code to be just C and it was easier then implementing a hash table from  
> scratch in C.
> 
> Performance  
> ---------------  
> To make sure using a Ruby hash table performed adequately, I ran some  
> performance tests.
> 
> Test - Create 100,000 Animal (implemented in an extension, taken from  
> the SWIG Ruby manual). The test machine was a Pentium M Thinkpad T41  
> laptop.
> 
> Test Time  
> No mappings 9.3 to 9.6 seconds  
> Mappings using Ruby hash 9.5 to 9.9 seconds  
> Mappings using std::map 30 seconds
> 
> You can see there is slight performance hit for tracking mappings  
> between C++ objects and Ruby object. Note the C++ map was so slow  
> because deletions took a lot of time (and there were 100,000 deletetions  
> by the end of the test run).
> 
> Summary  
> ---------  
> If people think it would be a good idea to add this functionality to  
> SWIG (whether using this implementation or some other one) then I'd be  
> glad to take the lead on this.
> 
> Thanks,
> 
> Charlie
> 
> Code  
> ---------------------  
> This is a new file called rubymappings.swg
> 
> /\* Global Ruby hash table to store mappings from C/C++  
> &nbsp;&nbsp;&nbsp;&nbsp;structs to Ruby Objects. \*/  
> static VALUE swig\_ruby\_mappings;
> 
> /\* Get a Ruby number to reference a pointer \*/  
> static VALUE SWIG\_RubyPtrToReference(void\* ptr) {  
> &nbsp;&nbsp;/\* We cast the pointer to an unsigned long  
> &nbsp;&nbsp;&nbsp;and then store a reference to it using  
> &nbsp;&nbsp;&nbsp;a Ruby number object. \*/
> 
> &nbsp;&nbsp;/\* Convert the pointer to a Ruby number \*/  
> &nbsp;&nbsp;unsigned long value = (unsigned long) ptr;  
> &nbsp;&nbsp;return LONG2NUM(value);  
> }
> 
> /\* Get a Ruby number to reference an object \*/  
> static VALUE SWIG\_RubyObjectToReference(VALUE object) {  
> &nbsp;&nbsp;/\* We cast the object to an unsigned long  
> &nbsp;&nbsp;&nbsp;and then store a reference to it using  
> &nbsp;&nbsp;&nbsp;a Ruby number object. \*/
> 
> &nbsp;&nbsp;/\* Convert the Object to a Ruby number \*/  
> &nbsp;&nbsp;unsigned long value = (unsigned long) object;  
> &nbsp;&nbsp;return LONG2NUM(value);  
> }
> 
> /\* Get a Ruby object from a previously stored reference \*/  
> static VALUE SWIG\_RubyReferenceToObject(VALUE reference) {  
> &nbsp;&nbsp;/\* The provided Ruby number object is a reference  
> &nbsp;&nbsp;to the Ruby object we want.\*/
> 
> &nbsp;&nbsp;/\* First convert the Ruby number to a C number \*/  
> &nbsp;&nbsp;unsigned long value = NUM2LONG(reference);  
> &nbsp;&nbsp;return (VALUE) value;  
> }
> 
> /\* Add a mapping from a C/C++ struct to a Ruby object \*/  
> static void SWIG\_RubyAddMapping(void\* ptr, VALUE object) {  
> &nbsp;&nbsp;/\* In a Ruby hash table we store the pointer and  
> &nbsp;&nbsp;the associated Ruby object. The trick here is  
> &nbsp;&nbsp;that we cannot store the Ruby object directly - if  
> &nbsp;&nbsp;we do then it cannot be garbage collected. So  
> &nbsp;&nbsp;instead we typecast it as a unsigned long and  
> &nbsp;&nbsp;convert it to a Ruby number object.\*/
> 
> &nbsp;&nbsp;/\* Get a reference to the pointer as a Ruby number \*/  
> &nbsp;&nbsp;VALUE key = SWIG\_RubyPtrToReference(ptr);
> 
> &nbsp;&nbsp;/\* Get a reference to the Ruby object as a Ruby number \*/  
> &nbsp;&nbsp;VALUE value = SWIG\_RubyObjectToReference(object);
> 
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;rb\_hash\_aset(swig\_ruby\_mappings, key, value);  
> }
> 
> /\* Get the Ruby object that owns the specfied C/C++ struct \*/  
> static VALUE SWIG\_RubyInstanceFor(void\* ptr) {  
> &nbsp;&nbsp;/\* Get a reference to the pointer as a Ruby number \*/  
> &nbsp;&nbsp;VALUE key = SWIG\_RubyPtrToReference(ptr);
> 
> &nbsp;&nbsp;/\* Now lookup the value stored in the Ruby hash table \*/  
> &nbsp;&nbsp;VALUE value = rb\_hash\_aref(swig\_ruby\_mappings, key);  
> &nbsp;&nbsp;  
> &nbsp;&nbsp;if (value == Qnil) {  
> &nbsp;&nbsp;&nbsp;&nbsp;return Qnil;  
> &nbsp;&nbsp;}  
> &nbsp;&nbsp;else {  
> &nbsp;&nbsp;&nbsp;&nbsp;/\* Convert this value to Ruby object \*/  
> &nbsp;&nbsp;&nbsp;&nbsp;return SWIG\_RubyReferenceToObject(value);  
> &nbsp;&nbsp;}  
> }
> 
> /\* Remove a mapping from a C/C++ struct to a Ruby object \*/  
> static void SWIG\_RubyRemoveMapping(void\* ptr) {  
> &nbsp;&nbsp;/\* Get a reference to the pointer as a Ruby number \*/  
> &nbsp;&nbsp;VALUE key = SWIG\_RubyPtrToReference(ptr);  
> &nbsp;&nbsp;VALUE object = SWIG\_RubyInstanceFor(ptr);
> 
> &nbsp;&nbsp;/\* Reset the C/C++ data struct associated with the Object.  
> &nbsp;&nbsp;This is needed in case a Ruby object exists longer than  
> &nbsp;&nbsp;its underlying C++ object. By setting the data\_struct  
> &nbsp;&nbsp;to nil, code in SWIG\_Ruby\_ConvertPtr can detect this problem  
> &nbsp;&nbsp;and return an error message, as opposed to causing a  
> &nbsp;&nbsp;segmentation fault.\*/
> 
> &nbsp;&nbsp;if (object != Qnil) {  
> &nbsp;&nbsp;&nbsp;&nbsp;DATA\_PTR(object) = 0;  
> &nbsp;&nbsp;}  
> &nbsp;&nbsp;&nbsp;&nbsp;  
> &nbsp;&nbsp;/\* Now delete the object from the hash table. To  
> &nbsp;&nbsp;do this we need to call the Hash.delete method  
> &nbsp;&nbsp;in Ruby. \*/  
> &nbsp;&nbsp;static VALUE delete\_function = rb\_intern("delete");  
> &nbsp;&nbsp;rb\_funcall(swig\_ruby\_mappings, delete\_function, 1, key);  
> }
> 
> /\* Setup a Ruby hash table to store mappings \*/  
> void SWIG\_RubyInitializeMappings() {  
> &nbsp;&nbsp;/\* Create a ruby hash table to store mappings from C++  
> &nbsp;&nbsp;objects to Ruby objects. Also make sure to tell  
> &nbsp;&nbsp;the garabage collector about the hash table \*/  
> &nbsp;&nbsp;swig\_ruby\_mappings = rb\_hash\_new();  
> &nbsp;&nbsp;rb\_gc\_register\_address(&swig\_ruby\_mappings);  
> }
> 
> Patches to Hook Into SWIG  
> ----------------------
> 
> # Index: ruby.cxx
> 
> RCS file: /cvsroot/swig/SWIG/Source/Modules/ruby.cxx,v  
> retrieving revision 1.78  
> diff -u -i -r1.78 ruby.cxx  
> --- ruby.cxx 17 Aug 2005 20:35:53 -0000 1.78  
> +++ ruby.cxx 18 Aug 2005 18:41:22 -0000  
> @@ -465,6 +465,9 @@  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;NIL);  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Printf(f\_init,"\n");
> 
> + // Initialize mapping code  
> + Printf(f\_init,"SWIG\_RubyInitializeMappings();\n");  
> +  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Language::top(n);
> 
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;/\* Finish off our init function \*/  
> @@ -1098,7 +1107,8 @@  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (current == CONSTRUCTOR\_INITIALIZE) {  
> &nbsp;&nbsp;&nbsp;&nbsp;String \*action = Getattr(n,"wrap:action");  
> &nbsp;&nbsp;&nbsp;&nbsp;if (action) {  
> - Append(action,"DATA\_PTR(self) = result;");  
> + Append(action,"DATA\_PTR(self) = result;\n");  
> + Append(action,"SWIG\_RubyAddMapping(result, self);\n");  
> &nbsp;&nbsp;&nbsp;&nbsp;}  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;emit\_action(n,f);  
> @@ -1850,6 +1860,8 @@  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Printf(freebody, "free((char\*) %s);\n", Swig\_cparm\_name(0,0));  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}  
> + Printf(freebody, tab4, NIL);  
> + Printf(freebody, "SWIG\_RubyRemoveMapping(%s);\n", Swig\_cparm\_name(0,0));  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Printv(freebody, "}\n", NIL);
> 
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Printv(f\_wrappers, freebody, NIL);
> 
> # Index: ruby.swg
> 
> RCS file: /cvsroot/swig/SWIG/Lib/ruby/ruby.swg,v  
> retrieving revision 1.29  
> diff -u -i -r1.29 ruby.swg  
> --- ruby.swg 1 Feb 2005 00:08:19 -0000 1.29  
> +++ ruby.swg 18 Aug 2005 18:44:19 -0000  
> @@ -6,6 +6,7 @@
> 
> &nbsp;&nbsp;%runtime "rubyhead.swg"  
> &nbsp;&nbsp;%runtime "swigrun.swg" // Common C API type-checking code  
> +%runtime "rubymappings.swg" /\* Stores mappings from C/C++ struct  
> to Ruby objects \*/  
> &nbsp;&nbsp;%runtime "rubydef.swg"
> 
> &nbsp;&nbsp;%insert(initbeforefunc) "swiginit.swg"
> 
> # Index: rubydef.swg
> 
> RCS file: /cvsroot/swig/SWIG/Lib/ruby/rubydef.swg,v  
> retrieving revision 1.17  
> diff -u -i -r1.17 rubydef.swg  
> --- rubydef.swg 1 Feb 2005 00:08:20 -0000 1.17  
> +++ rubydef.swg 18 Aug 2005 18:43:53 -0000  
> @@ -71,6 +71,13 @@
> 
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (!ptr)  
> &nbsp;&nbsp;&nbsp;&nbsp;return Qnil;  
> +   
> + /\* Have we already wrapped this pointer? \*/
> 
> + obj = SWIG\_RubyInstanceFor(ptr);
> 
> +
> 
> + if (obj != Qnil) {  
> + return obj;  
> + }
> 
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (type-\>clientdata) {  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;sklass = (swig\_class \*) type-\>clientdata;

---

<div class="post-metadata">

**Author:** ![Pit](https://avatars.discourse-cdn.com/v4/letter/p/f14d63/32.png) [@Pit](https://rubytalk.org/u/Pit)\
**Post date:** [19 August 2005 06:50 UTC](https://rubytalk.org/t/swig-ruby-memory-management/20265/2 "2005-08-19T06:50:58Z")

</div>

Charlie Savage schrieb:

> ...  
> As can be seen in the code below, I have gone down the hashtable route  
> with a slight twist - I use a Ruby hash table from C to store these  
> mappings. I did this instead of using a C++ map since I wanted to the  
> code to be just C and it was easier then implementing a hash table from  
> scratch in C.  
> ...

I can't say anything about your code, cause I've never used SWIG yet, but it looks like you've done a very nice work. I just wanted to mention a hash table implementation in C which is used in the Ruby source code: just look at st.[hc].

Regards, Pit
