DEFINITION MODULE DynamicItem;

(* Product: Incremental Storage Manager

   Version: 1.0

   Author:
        Daniel B. Hankins
        143 Montgomery Street
        Poughkeepsie, NY 12601
        dan-hankins@cup.portal.com

   Creation Date: 1989

   Release  Date: November 21, 1989

   Notice of Intellectual Property:
        This material is *NOT COPYRIGHTED*.  By this notice, I hereby place
   this program and all its parts in the public domain, under the definitions
   and restrictions of United States law.

   History of Revisions:
        None yet.
*)

FROM SYSTEM IMPORT ADDRESS;

TYPE
  Object;

  InitProc = PROCEDURE(ADDRESS);
  TermProc = InitProc;

PROCEDURE New(Size: LONGCARD; InitIt: InitProc; TermIt: TermProc): Object;
(* Size:   *in*  Size in bytes of chunk to be allocated
   InitIt: *in*  The procedure which will initialize the contents of the
                 chunk.  It is passed the address of the first byte of the
                 chunk.
   TermIt: *in*  The procedure which will clean up the contents of the
                 chunk when its storage is reclaimed.  For instance, if
                 the chunk contains variables of type DynamicItem.Object,
                 TermIt should DynamicItem.Dispose each of them so that
                 their reference counts can be accurately maintained.
           *out* The resulting object
*)

PROCEDURE Ref(Item: Object): Object;
(* Item: *in*  The chunk to which another reference is to be generated.
         *out* The chunk after its reference count is incremented.

   Explanations and Recommendations:
        Although the Modula-2 compiler will allow the programmer to perform
   assignment between variables of type DynamicItem.Object, she should not.
   This storage manager relies on a technique called reference counts.  The
   reference count (which is embedded in the header of a storage chunk) is
   intended to reflect the number of variables (pointers, actually) which
   are accessing the same chunk.  The reason for this is so that the
   storage manager will know when it can remove the chunk from the active
   list and reclaim the storage it consumed.
        So if variables A, B, and C are all of type DynamicItem.Object, and
   all reference the same storage chunk, then the reference count of the
   chunk should be three.
        To make two variables refer to the same object, use the following:

        A := DynamicItem.Ref(B);

   instead of

        A := B;

        If you want to copy the contents of an chunk A into another chunk
   B, then the programmer should create a new dynamic item object B, access
   the contents of both, and then copy the contents.  For instance:

        B := DynamicItem.New(ASize, AInit, ATerm);
        ACopy(DynamicItem.Access(A), DynamicItem.Access(B));

   where ACopy takes a pair of pointers to the kind of chunk being copied.
*)


PROCEDURE Dispose(Item: Object);
(* Item: *in* The chunk to be disposed of.

   Explanations and Recommendations:
        Dispose does *not* necessarily reclaim the storage used by a chunk.
   Dispose decrements the reference count of the chunk (see procedure Ref,
   above); there is now one less pointer to the chunk.  Only when there are
   no pointers left to a chunk (i.e. reference count = 0) does Dispose
   actually attempt to reclaim the storage used by the chunk.  Just
   before reclaiming that storage, it runs the TermIt procedure (see
   procedure New, above) stored in the header of the chunk against the
   contents of that chunk.  The TermIt procedure is meant to be used for
   Dispose-ing of any chunks the chunk may reference.
        For instance, suppose that the chunk the programmer is working
   with is a record which has three variables of type DynamicItem.Object.
   TermIt should DynamicItem.Dispose each of the three variables.
   Otherwise, the three chunks referenced will *never* be reclaimed;
   their reference counts will never go to zero, and they will become
   unusable dead storage.
*)

PROCEDURE Access(Item: Object): ADDRESS;
(* Item: *in*   The chunk which is to be accessed.
         *out*  The address of the contents of the chunk.

   Explanations and Recommendations:
        Whenever DynamicItem.New or DynamicItem.Dispose is called,
   the contents of a chunk may move in memory.  So it is a good idea to
   avoid code like this:

        ChunkPtr := DynamicItem.Access(ChunkVar);
        ...
        (* other code *)
        ...
        DynamicItem.Dispose(ChunkVar2);
        ChunkPtr^.XValue := 10;

   because ChunkPtr may no longer be pointing at the contents of the
   chunk after a New or Dispose is done.  A better practice is to
   *always* Access a chunk *just before* its contents are to be
   manipulated, like this:

        ChunkPtr := DynamicItem.Access(ChunkVar);
        ...
        (* other code *)
        ...
        DynamicItem.Dispose(ChunkVar2);
>>>     ChunkPtr := DynamicItem.Access(ChunkVar);
        ChunkPtr^.XValue := 10;
*)

PROCEDURE Set(Item:Object; NewDynItem: ADDRESS);
(* DO NOT USE THIS PROCEDURE; IT IS FOR THE EXCLUSIVE USE OF OTHER PARTS
   OF THE STORAGE MANAGER!
*)

PROCEDURE DisposeAll();
(* This procedure returns *all* storage allocated by the storage manager
   to the operating system.  It is a good idea to call this procedure
   just before exiting your program;  it makes sure you haven't forgotten
   to return any resources you've used.
*)

PROCEDURE NilObject(): Object;
(* This procedure returns an uninitialized chunk, which cannot be used
   except for testing to see if another chunk is uninitialized.  It is
   primarily for use of other storage manager procedures.
*)

PROCEDURE Nil(Item: Object): BOOLEAN;
(* This procedure returns TRUE if a chunk is uninitialized, FALSE 
   otherwise.
*)

END DynamicItem.
