17 Situated Cell-Like Objects

In this chapter, we implement class Celloids: an extension class for objects that behave essentially like cells; they have content which can be accessed (read) and assigned (written). The new challenge here is twofold: (1) during garbage collection the content of a class Celloid must also be copied, (2) we must ensure that only a local class Celloid can be mutated (for a non-local one, we should signal an error).

A class Celloid is situated. What does situated mean? Simply that the object resides in one specific constraint store, aka a computation space. If class Celloid C resides in space S1, and S2 is a subspace of S1 (i. e. is subordinated to S1), it is meaningful for a thread in S2 to access C since any constraint (therefore value) from S1 are visible in S2, but it is generally not meaningful for a thread in S2 to assign C since constraints (therefore values) specific to S2 are not visible from S1. Our implementation will enforce the appropriate restrictions.

17.1 Celloid Class

Again, we subclass class OZ_Extension.

#include "mozart.h" 
 
class Celloid : public OZ_Extension {
public:
  OZ_Term content;
  Celloid(OZ_Term t):content(t){}
  static int id;
  virtual int getIdV() { return id; }
  virtual OZ_Term typeV() { return OZ_atom("celloid"); }
  virtual OZ_ExtensiongCollectV();
  virtual OZ_ExtensionsCloneV();
  virtual void gCollectRecurseV();
  virtual void sCloneRecurseV();
  virtual OZ_Term printV(int depth = 10);
};

17.2 Celloid Creation

The celloid_new builtin takes one input argument t, creates a new celloid whose content is initialized to t, boxes it and returns the result.

OZ_BI_define(celloid_new,1,1)
{
  OZ_declareTerm(0,t);
  OZ_RETURN(OZ_extension(new Celloid(t)));
}
OZ_BI_end

17.3 Type Testing

The definitions here are similar to the ones presented earlier for the class Counter class.

int Celloid::id;
 
inline OZ_Boolean OZ_isCelloid(OZ_Term t)
{
  t = OZ_deref(t);
  return OZ_isExtension(t) &&
    OZ_getExtension(t)->getIdV()==Celloid::id;
}
 
OZ_BI_define(celloid_is,1,1)
{
  OZ_declareDetTerm(0,t);
  OZ_RETURN_BOOL(OZ_isCelloid(t));
}
OZ_BI_end

17.4 Expecting Celloid Arguments in Builtins

Again this is similar to the class Counter class: we define an unboxing function and a convenience macro.

inline CelloidOZ_CelloidToC(OZ_Term t)
{
  return (Celloid*) OZ_getExtension(OZ_deref(t));
}
 
#define OZ_declareCelloid(ARG,VAR) \
OZ_declareType(ARG,VAR,Celloid*,"celloid",OZ_isCelloid,OZ_CelloidToC)

17.5 Operations on Celloids

First, we provide an access builtin that retrieves the content of the celloid.

OZ_BI_define(celloid_access,1,1)
{
  OZ_declareCelloid(0,c);
  OZ_RETURN(c->content);
}
OZ_BI_end

Second, we provide an assign builtin that sets the content of the celloid. This operation should only be allowed for a thread executing in the home space of the celloid. For a thread executing in a subordinated space, an exception will be raised.

OZ_BI_define(celloid_assign,2,0)
{
  OZ_declareCelloid(0,c);
  OZ_declareTerm(1,t);
  if (c->isLocal()) { c->content=t; return PROCEED; }
  else return OZ_raiseErrorC("celloid",3,OZ_atom("nonLocal"),
                             OZ_in(0),OZ_in(1));
}
OZ_BI_end

virtual member function isLocal() indicates whether the current space is the home space of the celloid. If yes, we set the content to the given argument; if no, we raise an error. OZ_in(n) refers to the nth input argument of the builtin.

17.6 Printing Support

We provide here only minimal printing support.

OZ_Term Celloid::printV(int depth = 10)
{
  return OZ_atom("<celloid>");
}

17.7 Garbage Collection

The first part of garbage collection is as before: we create a new instance of class Celloid initialized with the current content of the celloid that is being copied by gc.

OZ_ExtensionCelloid::gCollectV() { return new Celloid(content); }

The second part involves recursively copying the content of the celloid. This is implemented in a different virtual function:

void Celloid::gCollectRecurseV() { OZ_gCollect(&content); }

The procedure OZ_gCollect(OZ_Term*) performs the gc copy and update of its argument.

You may wonder: why not perform the recursive copy of the content in gCollectV() itself. Under no circumstances should you do this! It would break essential invariants in the garbage collector. GC copy must proceed in these 2 phases:

  1. gCollectV() creates a new instance (on the to heap) and initializes it with the current contents of the object being gced.

  2. gCollectRecurseV() is at some subsequent point invoked on the new instance and should perform the gc copy and update of its contents.

17.8 Cloning

Cloning is used to produce a copy of a computation space. It has the same structure, and the underlying implementation in fact shares most of the code with, garbage collection.

OZ_ExtensionCelloid::sCloneV() { return new Celloid(content); }
void Celloid::sCloneRecurseV() { OZ_sClone(&content); }

17.9 Native Functor

Again, we proceed as before:

OZ_C_proc_interface * oz_init_module(void)
{
  static OZ_C_proc_interface table[] = {
    {"new",1,1,celloid_new},
    {"is",1,1,celloid_is},
    {"access",1,1,celloid_access},
    {"assign",2,0,celloid_assign},
    {0,0,0,0}
  };
  Celloid::id = OZ_getUniqueId();
  return table;
}

Assuming the code above is put in file celloid.cc, we first compile and then create a DLL as follows

oztool c++ -c celloid.cc
oztool ld celloid.o -o celloid.so-`oztool platform`

17.10 Oz Wrapper Module

Here, we hardly need an Oz wrapper module. Unlike for counter objects, we don't need to register celloid for finalization: there are no resources off the heap. However, we can provide a nice error print formatter for the case when an access is attempted from without the celloid's home space.

functor 
import 
   Celloid(new:New is:Is access:Access assign:Assign)
   at 'celloid.so{native}' 
   Error(registerFormatter)
export 
   New Is Access Assign
define 
   fun {CelloidFormatter E}
      T = 'Celloid Error' 
   in 
      case E of celloid(nonLocal C V) then 
         error(kind: T
               msg: 'Attempted assign on non local celloid' 
               items: [hint(l:'Operation' m:'Celloid.assign')
                       hint(l:'Celloid'   m:oz(C))
                       hint(l:'Value'     m:oz(V))])
      else 
         error(kind: T
               items: [line(oz(E))])
      end 
   end 
   {Error.registerFormatter celloid CelloidFormatter}
end


Denys Duchier, Leif Kornstaedt and Christian Schulte
Version 1.4.0 (20080702)