-- Transport Mechanism Filestore - Heap reader allocation --

-- [Juniper]<Grapevine>MS>ReaderAlloc.mesa

-- Randy Gobbel		19-May-81 11:56:01 --
-- Andrew Birrell	24-Feb-81 16:24:23 --

DIRECTORY
HeapDefs	USING[ objectStart ],
HeapXDefs	USING[ ReaderData, RestartReader, StopReader ],
LogDefs		USING[ DisplayNumber, WriteChar ],
ObjectDirDefs	USING[ UseObject, FreeObject, noObject ],
ObjectDirXDefs	USING[ ObjectNumber, ObjectBase ],
Process		USING[ InitializeMonitor, InitializeCondition,
		       DisableTimeout ],
Storage		USING[ Node ],
VMDefs		USING[ ReadPage, Release ];

ReaderAlloc: MONITOR
   IMPORTS HeapXDefs, LogDefs, ObjectDirDefs, ObjectDirXDefs, Process,
           Storage, VMDefs
   EXPORTS HeapDefs, HeapXDefs =
BEGIN

ReaderData: PUBLIC TYPE = HeapXDefs.ReaderData;
Handle: TYPE = POINTER TO ReaderData;

Allocate: PROCEDURE[CARDINAL] RETURNS[POINTER] = Storage.Node;

noReader: Handle = NIL;

readerChain, freeChain: Handle ← noReader;
readerCount: CARDINAL ← 0;

HeapStartRead: PUBLIC ENTRY PROCEDURE[ from: ObjectDirXDefs.ObjectNumber ]
                              RETURNS[ res: Handle ] =
   BEGIN
   IF freeChain = noReader
   THEN BEGIN
        res ← Allocate[SIZE[HeapXDefs.ReaderData]];
        Process.InitializeMonitor[@(res.LOCK)];
        END
   ELSE BEGIN
        res ← freeChain; freeChain ← freeChain.next;
        END;
   res.next ← readerChain; readerChain ← res;
   res.object ← from; ObjectDirDefs.UseObject[from];
   res.end ← FALSE;
   res.stopped ← from=compactorNumber;
   Process.InitializeCondition[@res.canStart,0];
   Process.DisableTimeout[@res.canStart];
   res.offset ← HeapDefs.objectStart;
   res.where ← ObjectDirXDefs.ObjectBase[from];
   res.page ← VMDefs.ReadPage[ res.where.page, 0--lookAhead-- ];
   readerCount ← readerCount + 1; LogDefs.WriteChar['{];
   END;

HeapEndRead: PUBLIC ENTRY PROCEDURE[ from: Handle ] =
   BEGIN
   -- Note that, in the unlikely event that the user was calling         --
   -- 'HeapReadData' from several processes, he must have returned from  --
   -- all such calls before calling 'HeapEndRead', or else one of the    --
   -- calls may commence after the reader has been freed.  Hence, we may --
   -- update fields of the 'Handle' without invoking the lock on   --
   -- its monitored record                                               --
   VMDefs.Release[from.page];
   ObjectDirDefs.FreeObject[from.object];
   BEGIN -- remove from 'readerChain' --
      prev: POINTER TO Handle ← @readerChain;
      WHILE prev↑ # from DO prev ← @(prev↑.next) ENDLOOP;
      prev↑ ← from.next;
   END;
   from.next ← freeChain; freeChain ← from;
   readerCount ← readerCount - 1; LogDefs.WriteChar['}];
   END;


-- interlock with compactor --

compactorNumber: ObjectDirXDefs.ObjectNumber ← ObjectDirDefs.noObject;

StopAllReaders: PUBLIC ENTRY PROCEDURE[ obj: ObjectDirXDefs.ObjectNumber ] =
   BEGIN
   ptr: Handle ← readerChain;
   UNTIL ptr = noReader
   DO IF ptr.object = obj THEN HeapXDefs.StopReader[ptr];
      ptr ← ptr.next
   ENDLOOP;
   compactorNumber ← obj;
   END;

RestartAllReaders: PUBLIC ENTRY PROCEDURE[ obj: ObjectDirXDefs.ObjectNumber ] =
   BEGIN
   ptr: Handle ← readerChain;
   UNTIL ptr = noReader
   DO IF ptr.object = obj THEN HeapXDefs.RestartReader[ptr];
      ptr ← ptr.next
   ENDLOOP;
   IF obj = compactorNumber THEN compactorNumber ← ObjectDirDefs.noObject;
   END;

LogDefs.DisplayNumber[ "Readers"L, [short[@readerCount]] ];

END.