Revisiting this topic given newly discovered issues: Back in January I had a solution that worked quite well with passing in Ruby objects to the C library. Ruby objects could be attached to Swig wrappers around structs so that their lifecycle's were kept; i.e., when C object X was garbaged collected, its wrapper would go away, and associated objects Y & Z were reaped since they no longer had a parent.
However, it's now come up that the Swig wrappers themselves don't seem to live a terribly long time in some cases. Also, asking Swig for the same struct more than once results in a completely different Swig wrapper coming back; i.e., there's no one-to-one mapping between the Swig wrapper and the underlying struct. And, the big problem, the Ruby object passed to the C structs are still vulnerable to being reaped as a result of this. To prove this, I created two test APIs to store and retrieve a single pn_transport_t instance [1]. I create a transport, store it and then fetch it again and get two different objects that are are not equal: t = Cproton.pn_transport => t now holds a reference to an instance of pn_transport_t Cproton.pn_transport_set_mine t => that pn_transport_t struct is now pointed to in the C libraries u = Cproton.pn_transport_get_mine => u now holds a reference to the same instance of pn_transport_t t == u => false -- comparison fails Additionally, the pn_record_get API seems to have an issue with retrieving stored objects that may or may not be related to this issue. I've written a reproducer [2] that simply stores and retrieves attachments for a transport, creates a hash, forces garbage collection and then shows object statistics for the MyHash and wrapper for the pn_record_t type in Ruby space. Specifically, if lines 101 and 103-4 are commented out, the reproducer runs fine and creates a whole lot of new transports before going back for the original and getting its attachments. If lines 102 thru 104 are commented out the reproducer runs fine and retrieves the same attachments over and over without a problem. BUT, if 101-2 are commented out and the reproducer alternates between getting the stored one and a new one, it blows up on the first call to retrieve the stored reference after getting a single new transport. This doesn't seem to make sense since I can create 1k new transports and then get my old one back, but creating a single new one and then getting the old one back it blows up. And where it blows up is on displaying the retrieved attrs object. I know that this issue is related to garbage collection: I can add the attrs variable to a global array and the error never happens in the last scenario. But this doesn't seem to be a good solution since it would require a lot of extra code to track and remove objects when a C struct is deleted. So, in the end, I need a better way to store and retrieve Ruby objects in the library than is currently being done. It works well with the engine APIs since you're not creating and then re-creating objects, so there's not a lot of storing and fetching going on. But with the reactive APIs, it wants to store object state details and recreate them via the various class wrap methods. And this is failing for Ruby since Ruby's mark and sweep is deleting objects that C is holding. [1] pn_transport_set_mine, pn_transport_get_mine [2] http://github.com/mcpierce/Proton/blob/PROTON-799-Ruby-engine-apis/examples/ruby/wrapper_test.rb -- Darryl L. Pierce, Sr. Software Engineer @ Red Hat, Inc. Delivering value year after year. Red Hat ranks #1 in value among software vendors. http://www.redhat.com/promo/vendor/
pgpCQP9zPoED5.pgp
Description: PGP signature
