The model allows to implement serializable objects that would be managed, persistent, could migrate, safely reference each other, be replicated, garbage collected, etc. Migration and replication groups of objects are defined implicitly as transitive closures of persistent pointers that are treated, for purposes of migration/replication group enumeration, as if they were bidirectional. The introspection API allows to access object metainformation at run-time. This is utilized by the Core for implementation of the features above but could be used by an application too. The object to object migration is a primary way of object collaboration. The RPC is built on top of migrations.
One of the key features the Core provides is the automatic data serialization. There is a set of predefined primitive types that must be used by the user instead of the native C++ types because the latter do not implement the serialization interface. These types are called the properties and are derived from Massiv::Core::Property. These properties can be simple integers, floats, strings or complex compound types that were built from other properties. If their structure can be changed at run-time they are called containers. Currently arrays, dictionaries and sets have been implemented. Containers always hold stored properties by value and are strongly typed (i.e. array of integers, array of pointers, set of strings, etc.). Any property is serializable without an user's assistance.
Generally, properties can by hierarchized into tree-like dynamic structures (properties can be added/removed at run-time). Each property maintains a reference to the owner property and various replication related attributes. When property value changes, the write operation is propagated to the root of the tree and the owner properties are thus notified that the value has changed. This allows an efficient replication of the whole hierarchy as only the subtrees that have been modified since the last replication update need be serialized. A property hierarchy can be enumerated at run-time.
![]() | Note |
---|---|
When talking about a hierarchy of properties, we do not mean some dynamic tree-like structure managed by the programmer. The programmer uses standard constructs - classes with propertie,s container properties, etc. Because of the way the property types are implemented, the Core knows everything about property hierarchy - it can easily determine list of properties that are members of an object or a container, which object or container owns given property, etc. And that's the hierarchy that we talk about. The hierarchy can change at run-time, because properties can be added and removed from containers. |
The Core allows to define objects that will be fully managed by the Core on the application level. Any such object is a special kind of property and must be derived from Massiv::Core::Object (or a subclass). Managed objects can contain other properties as their data members, including managed objects, or can inherit from other managed objects. Multiple inheritance is allowed too as far as Massiv::Core::Object is inherited exactly once. The structure of managed objects, mainly the inheritance hierarchy and data members, must be described in external .idl files. IDL files are used to generate helper classes that are utilized by the Core to perform automatic object instantiation, serialization and the like. See Chapter 9, Introduction to IDL for more information about the IDL.
Managed objects can be classified according to the type of their usage, instantiation and lifetime.
Objects with a pointer semantics
They are often instantiated as "stand-alone" (addressable, on the heap) objects. Such objects have their unique identification (Massiv::Core::ObjectId), can migrate and can be referenced by persistent pointers. They are often accessed through pointers only and are destroyed by the garbage collector.
Objects with a value semantics
They are instantiated on the stack, owned by other objects or containers. Such objects are not addressable, can not migrate and can not be referenced. They are used as "value objects". Their life time corresponds to the lifetime of the owner object or, if instantiated simply on the stack, standard scoping rules.
Throwable objects
Managed exceptions objects, exceptions thrown by application code or the Core itself. The Core is able to manage and propagate them to caller nodes during remote calls.
![]() | Note |
---|---|
Under special circustances, objects with the value semantics can be instantiated also as "stand-alone" objects and objects with pointer semantics can be instantiated on the stack using a special construct. This is because the Core can instantiate any property either "stand-alone" (on the heap) or on the stack. The only thing that must be ensured is a proper object initialization. The above taxonomy classifies objects according to their "most common usage" or "how they should be used". The classification could be done on the per-instance basis. |
The Core implements a concept of persistent pointers to objects. These pointers encapsulate Massiv::Core::ObjectId and can point to components (super classes) of addressable objects only. In this way they are similar to Java references.
The pointers can have either local or remote dereference semantics. The pointers with the local semantics are able to access local objects (including local replicas) and fail to dereference if the target object is remote. The access to both methods and attributes is gained (in other words everything that could be accessible through a corresponding native C++ pointer would be accessible too). The pointers with the remote semantics access RPC methods of referenced objects. The important thing is that both local and remote objects are referenced by the managed pointers and the C++ native pointers must not be used at all.
Pointers can also be either strong or weak. This is related to the garbage collector and the way how pointers are interpreted by it. GC periodically scans for unreachable objects (objects unreferenced by strong pointers), and deletes them. Scanning originates from the program stack and special GC root objects. See also Section 5.4, “Garbage Collector”.
Strong pointers can reference local objects only. Thus they also implicitly define migration groups of objects. If an object A that strongly references object B migrates (or B migrates), both A and B will migrate. This ensures that strong pointers will always point to local objects.
Pointers can be annotated by pointer replication flags. When enumerating the replication group of an object, pointer replication flags mask must be given. Replication group is then defined as a transitive closure of bidirectional pointers that match specified replication flags mask.
![]() | Note |
---|---|
Automatic (implicit) migration and replication group definition/management is a key property of the Core that makes it unique. It gives a programmer an opportunity to easily partition his objects to compact groups and isolate data flow between the groups (a programmer can thus express his intention that a referenced object should always be local in respect to the object holding the relevant pointer; access to such object can be optimized then). The implicit definition of replication groups offers an easy-to-use object replication for free. The groups change at run-time according to pointer changes. |