-- Copyright (C) 1981, 1984, 1985  by Xerox Corporation. All rights reserved. 
-- ReaderAlloc.mesa, Transport Mechanism Filestore - Heap reader allocation

-- HGM,	15-Sep-85  3:34:47
-- Randy Gobbel		19-May-81 11:56:01 --
-- Andrew Birrell	24-Feb-81 16:24:23 --
-- Brenda Hankins	14-Aug-84 10:02:50	add unwind on HeapStartRead

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 = LONG 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
    ENABLE UNWIND => NULL;
    IF freeChain = noReader THEN {
      res ← Allocate[SIZE[HeapXDefs.ReaderData]];
      Process.InitializeMonitor[@(res.LOCK)]}
    ELSE {res ← freeChain; freeChain ← freeChain.next};
    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: LONG 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.