The objects under the Universal Tree of a WorkingSet can either be mutable or immutable.
Only the mutable objects can be the target of update operations.
The immutable objects are called Replicate On Demand (ROD) objects, because they aren't sent in the operations ("deltas") which created them (i.e. in the deltas which are written or read by a DeltaRW session object).
Rather the propagation of ROD objects is handled by a different session object called a
RodSession (or at a lower level with RodWriter
and
RodReader
objects).
Under the hood, the sending/receiving of ROD objects is straightforward because they are immutable. There is no need to be concerned with versions, vector times, operations and Operational Transformation.
A WorkingSetMachine has a boolean flag which determines whether it is a custodian of the ROD objects. A custodian is eagerly sent ROD objects from its peers so it tends to get its own copy of them all.
A concrete class which implements interface IPersistable
determines whether object instances of that class
are ROD objects using the <<rod>>
directive. For example:
$struct+ TBuffer <<rod>> isa ceda::IPersistable :
model
{
ceda::string8 Buffer;
}
{
};
Consider a ROD object data type such as RImage
:
$struct+ RImage <<rod>> isa IPersistable :
model
{
JPeg jpeg;
}
{
};
where JPeg
is a reflected data type suitable for use in models of IPersistable
objects
(for example it needs to support default construction and serialisation).
It's often appropriate to cref
a ROD object from a mutable object.
This is perhaps what James Coplien calls the Envelope and Letter Idiom.
For example:
$struct+ PImage isa IPersistable :
model
{
xstring Name;
int32 Width;
int32 Height;
cref<RImage> Ref;
}
{
};
Consider a class ImageView
which caches an OpenGL texture using a $cache
function.
The return value of this function uses std::optional
to account for the fact that the texture is
uploaded into video memory asynchronously.
The implementation of this function calls AsyncGet()
on a cref
variable that points
at a ROD object.
This can cause a request for the ROD object to be sent by a RodSession
to a peer, and the
Texture
is uploaded as soon as the ROD object is received.
class ImageView
{
$cache^ std::optional<Texture> GetOpenGLTexture() const
{
if (const RImage* i = image.Ref.read().AsyncGet())
{
return UploadTexture(i->jpeg);
}
else
{
return std::nullopt;
}
}
PImage& image;
};
Note that AsyncGet()
on a cref
calls AsyncBind
on the
PSpace.