I currently have two strategies implemented for serialization and persistence. Both require storing the runtime in a thread local variable 'currentRuntime'. They fetch the runtime from there when they need it via a Ruby.getCurrentRuntime() call. I would like to hear your critiques and comments.

First (least intrusive) strategy:
Decouple an instance from the runtime by making RubyObject.metaClass transient, and
Add a RubyObject.metaClassName field

When an object is serialized/persisted, it goes alone. When it is deserialized, it uses the metaClassName to fetch its metaclass from the runtime. Voila! It's ready to go

Second, more intrusive strategy:
Leave the object coupled to its metaClass by leaving the metaClass variable non-transient. Decouple the metaclass from the runtime by making the RubyClass/ RubyModule marshal, allocator, methods, superclass, and parent fields transient. Add a new transient field in RubyModule called 'loadedByRuntime' with a default value of true.
Leave only the RubyModule.classId field serializable.
Serialize/persist the object and its metaclass.

When the object is deserialized/reloaded, it brings along a duplicate, decoupled instance of the metaclass with it. On the first method invocation, RubyObject.callMethod checks the 'loadedByRuntime' boolean and swaps the object's reference to its metaclass with a reference to the original. The deserialized metaclass is then garbage collected and the object is hooked up to its original metaclass and is ready to go again.

Pros and Cons
The first strategy is simple but requires and additional instance field 'metaClassName' in RubyObject. Code that is only touched in a couple of places: 1) the constructor, when it sets the metaClass variable, also sets the metaClassName. 2) the getMetaClass() accessor in RubyObject. It lazily initializes when the metaClass is null and the metaClassName isn't.
It trades space in every object for the simplicity of the solution

The second strategy only requires an additional field in RubyModule: the transient 'loadedByRuntime' flag. However, to get rid of the duplicate metaclass requires switching the reference at runtime and the check for that is done in callMethod() on every method invocation. This happens on every object, not just the persistent ones. Code is touched in several places, but is mostly in RubyModule and RubyClass. 1) The accessors in RubyModule cause a swap of all of the references to the allocator, marshal, methods, etc. on the first invocation of a method in the deserialized object. The persisted metaclass is now ready to go. 2) RubyObject.callMethod calls a 'MetaClassSwapper' with tasks (kinda like Runnables) that are configured at launch. One task switches the metaclasses and would be needed by serialzation to get rid of the duplicate metaclass 3) The second task is one that we would post to the swapper. It switches what we call the POM (persistent object memory) Ids. Once the POM Ids are switched, the persisted metaclass will not be reloaded along with the persistent object.

I prefer the first solution. It's simpler. Whaddya think? I will be happy to post a diff showing what I have done.


Reply via email to