[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: ADT Lib: Persistence



> [...]
> It probably also makes sense to have special riders in StdTypes. 

I agree. Fortunately, we can integrate this rather nicely with OOC's
library, by creating extensions of BinaryRider.Reader/Writer, which
have the methods ReadObject and WriteObject (see attached module
AcObject, formerly known as StdObjects).  Then Load/Store work with
the abstract classes ObjectReader and ObjectWriter.

Any concrete implementation of these abstract classes also store the
context for the externalize and interalize process, removing the need
for global variables, or an additional context.

> Special functions provided by Stores.Readers are:
> - PROCEDURE (VAR rd : Reader) TurnIntoAlien (cause : INTEGER)
> This causes the currently Internalized store to become Alien.
> - PROCEDURE (VAR rd : Reader) ReadVersion (min, max: INTEGER; OUT version:
> INTEGER)
> This reads a version identifier and cancels the current Internalize if it
> is not between <min> and <max>.

These will be part of AcObject.ObjectReader (they are not part of the
attached sample code yet).

> - a boolean variable "cancelled", which indicates that an Internalize has
> been cancelled. A smart Internalize function will periodically check its
> reader's cancelled status to see if it should exit without completing the
> entire process. Normally this is caused by (1) an inappropriate version
> number, (2) the Store being turned into an Alien.
> - a boolean variable "readAlien" which is true if an Alien has been read
> since the Reader was connected to a File.

These would be part of an implementation of AcObject.ObjectReader, but
should they be part of the abstract class, too?  It makes sense, that
`Load' can abort after a `cancel', so `canceled' should appear in the
abstract class.  I would rather try to keep `readAlien' out of
AcObject, though.

> 
> Special functions provided by Stores.Writers are:
> - PROCEDURE WriteVersion (version: INTEGER)

A method of AcObject.ObjectWriter.


A nice thing about my current outline is, that it moves the algorithm
of writing the objects into a separate class.  Except for
`Object.mark', all remnants of a particular externalization algorithm
have been moved out of `AcObject'.  (I would like to get rid of `mark'
altogther.)  

The attached module `Storage' (got any better name?) is a sketch of an
implementation of the "simple" externalization.  Too externalize a
bunch of objects, one would 

 - call Storage.ConnectWriter to get the writer
 - call writer.WriteObject(obj) for all objects in the root set
 - call writer.Disconnect to finish and clean up things

We can even have multiple externalization formats, e.g. an additional
one dealing with libraries.  Well, it is an idea, don't know if it is
feasible.

> How widely available is mmap() under Unix? 

It doesn't appear in the index of the glibc1 docs, but `man mmap' on
my Linux box claims

CONFORMING TO
       SVr4, POSIX.1b (formerly POSIX.4), 4.4BSD.  Svr4 documents
       additional error codes ENXIO and ENODEV.

Since it is POSIX, it is likely present in most recent implementations
of Unix.

-- mva


------------------------------------------------------------------------
MODULE AcObject;

IMPORT
  BinaryRider;


TYPE
  Object* = POINTER TO ObjectDesc;
  ObjectDesc* = RECORD
    mark*: LONGINT;  (* used by persistence mechanism; DO NOT TOUCH! *)
  END;

TYPE  (* abstract types, used for persistence mechanism (see Load/Store) *)
  ObjectReader* = POINTER TO ObjectReaderDesc;
  ObjectReaderDesc* = RECORD
    (BinaryRider.ReaderDesc)
  END;
  ObjectWriter* = POINTER TO ObjectWriterDesc;
  ObjectWriterDesc* = RECORD
    (BinaryRider.WriterDesc)
  END;


PROCEDURE Init* (obj: Object);
  BEGIN
    obj. mark := 0
  END Init;

PROCEDURE Destroy* (obj: Object);
  BEGIN
    obj. mark := 0
  END Destroy;


PROCEDURE (obj: Object) Store* (w: ObjectWriter);
(* Stores data of `obj' to `w'.  Nested record pointers are stored by calling 
   WriteObject.
   pre: This procedure is either activated by a super call, or from the
   procedure `WriteObject'.  *)
  BEGIN
  END Store;

PROCEDURE (obj: Object) Load* (r: ObjectReader);
(* Loads data of `obj' from `r'.
   pre: This procedure is either activated by a super call, or from the
   procedure `ReadObject'.  *)
  BEGIN
  END Load;


PROCEDURE (w: ObjectWriter) WriteObject* (obj: Object);
  BEGIN
  END WriteObject;
  
PROCEDURE (w: ObjectReader) ReadObject* (VAR obj: Object);
  BEGIN
  END ReadObject;
  
END AcObject.

------------------------------------------------------------------------

MODULE Storage;

IMPORT
  Channel, Kernel, Types, AcObject;

TYPE
  ObjectReader* = POINTER TO ObjectReaderDesc;
  ObjectReaderDesc* = RECORD
    (AcObject.ObjectReaderDesc)
    objCounter: LONGINT;
    nodeTab: ARRAY 999(*this should be an open array*) OF AcObject.Object;
  END;
  ObjectWriter* = POINTER TO ObjectWriterDesc;
  ObjectWriterDesc* = RECORD
    (AcObject.ObjectWriterDesc)
    objCounter: LONGINT;
    nodeTab: ARRAY 999(*this should be an open array*) OF AcObject.Object;
  END;


CONST
  unmarked = 0;  (* used by WriteObject *)


PROCEDURE ConnectReader* (ch: Channel.Channel): ObjectReader;
  VAR
    r: ObjectReader;
  BEGIN
    NEW (r);
(*    InitReader currently not exported by BinaryRider...
    BinaryRider.InitReader (r, ch, BinaryRider.littleEndian);*)
    r. objCounter := 0;
    r. nodeTab[0] := NIL;
    IF (r. base = NIL) THEN
      RETURN NIL
    ELSE
      RETURN r
    END
  END ConnectReader;
  
PROCEDURE ConnectWriter*(ch: Channel.Channel): ObjectWriter;
  VAR
    w: ObjectWriter;
  BEGIN
    NEW (w);
(*    InitWriter currently not exported by BinaryRider...
    BinaryRider.InitWriter (w, ch, BinaryRider.littleEndian);*)
    w. objCounter := 0;
    w. nodeTab[0] := NIL;
    IF (w. base = NIL) THEN
      RETURN NIL
    ELSE
      RETURN w
    END
  END ConnectWriter;


PROCEDURE (r: ObjectReader) ReadObject* (VAR obj: AcObject.Object);
  VAR
    mark: LONGINT;
    name: ARRAY 256(*should be unlimited*) OF CHAR;
    module: Kernel.Module;
    type: Types.Type;
  BEGIN
    r. ReadNum (mark);
    IF (mark <= 0) THEN
      obj := r. nodeTab[mark]
    ELSE
      r. ReadString (name);
      module := Kernel.modules;
      WHILE (module. name^ # name) DO
        module := module. next
      END;
      (* for now: trap if `name' does not exist *)
      r. ReadString (name);
      type := Types.This (module, name);
      (* for now: trap if `name' does not exist *)
      Types.NewObj (obj, type);
      r. nodeTab[mark] := obj;
      obj. Load (r)
    END
  END ReadObject;

PROCEDURE (w: ObjectWriter) WriteObject* (obj: AcObject.Object);
  VAR
    type: Types.Type;
  BEGIN
    IF (obj = NIL) THEN
      w. WriteNum (0)
    ELSIF (obj. mark # unmarked) THEN
      w. WriteNum (-obj. mark)
    ELSE  (* first occurence of this object *)
      INC (w. objCounter);
      obj. mark := w. objCounter;
      w. WriteNum (w. objCounter);
      type := Types.TypeOf (obj);
      w. WriteString (type. module. name^);
      w. WriteString (type. name^);
      obj. Store (w)
    END
  END WriteObject;
  
END Storage.