-- File: VMStorageMgr.mesa
-- Last edited by Levin:  25-Nov-81 14:36:35

DIRECTORY
  AllocDefs USING [
    AddSwapStrategy, RemoveSwapStrategy, SwappingProcedure, SwapStrategy,
    TryCodeSwapping],
  DynamicZone USING [CreateZone, DestroyZone, FinalizeZones, InitializeZones],
  SegmentDefs USING [
    DataSegmentAddress, DataSegmentHandle, DataSegmentType, DefaultMDSBase,
    DeleteDataSegment, HardDown, InsufficientVM, MakeDataSegment, VMtoDataSegment],
  VMSpecial USING  [PruneCache],
  VMStorage USING [];

VMStorageMgr: MONITOR
  IMPORTS AllocDefs, DynamicZone, SegmentDefs, VMSpecial
  EXPORTS VMStorage =

  BEGIN


  -- Types and Related Constants --

  PageBuffer: TYPE = RECORD [link: PagePointer, rest: ARRAY [1..255] OF WORD];

  PagePointer: TYPE = POINTER TO PageBuffer;

  ListState: TYPE = {stable, unstable};


  -- Global Variables --

  pageList: PagePointer;

  listState: ListState;
  listStable: CONDITION;

  swap: AllocDefs.SwapStrategy;

  -- Miscellaneous Declarations --

  vmMiscDS: SegmentDefs.DataSegmentType = 73B;

  NoMemory: ERROR [needed: CARDINAL] = CODE;


  -- Procedures and Variables Exported to VMStorage --

  -- Node-Level Allocator --

  longTerm, shortTerm: PUBLIC MDSZone;

  -- Page-Level Allocator --

  AllocatePage: PUBLIC PROCEDURE RETURNS [POINTER] =
    -- allocates a single page of main memory, returning its address.
    BEGIN
    page: PagePointer;
    GrabList[];
    IF pageList = NIL THEN 
      BEGIN OPEN SegmentDefs;
      seg: DataSegmentHandle;
      ReleaseList[];
      seg ← MakeDataSegment[DefaultMDSBase, 1, HardDown
				! InsufficientVM => ERROR NoMemory[needed]];
      seg.type ← vmMiscDS;
      page ← LOOPHOLE[DataSegmentAddress[seg]];
      END
    ELSE {page ← pageList; pageList ← page.link; ReleaseList[]};
    RETURN[page]
    END;

  FreePage: PUBLIC PROCEDURE [p: POINTER] =
    -- releases the single page beginning at 'p'.
    BEGIN
    page: PagePointer = LOOPHOLE[p];
    GrabList[];
    page.link ← pageList;
    pageList ← page;
    ReleaseList[];
    END;

  InitializeStorage: PUBLIC PROCEDURE =
    -- initializes the main memory allocator.
    BEGIN
    InitializePageLevel[];
    InitializeNodeLevel[];
    END;

  FinalizeStorage: PUBLIC PROCEDURE =
    -- finalizes the main memory allocator.
    BEGIN
    FinalizeNodeLevel[];
    FinalizePageLevel[];
    END;


  -- Internal Procedures --

  -- Node-Level Allocator --

  InitializeNodeLevel: PROCEDURE =
    BEGIN OPEN DynamicZone;
    InitializeZones[];
    longTerm ← CreateZone[id: "VM long term"L];
    shortTerm ← CreateZone[id: "VM short term"L];
    END;

  FinalizeNodeLevel: PROCEDURE =
    BEGIN OPEN DynamicZone;
    DestroyZone[shortTerm];
    DestroyZone[longTerm];
    FinalizeZones[];
    END;


  -- Page-Level Allocator --

  InitializePageLevel: PROCEDURE =
    BEGIN
    pageList ← NIL;
    listState ← stable;
    listStable.timeout ← 0;
    swap.proc ← FlushList;
    AllocDefs.AddSwapStrategy[@swap];
    END;

  FinalizePageLevel: PROCEDURE =
    BEGIN
    GrabList[];
    AllocDefs.RemoveSwapStrategy[@swap];
    UNTIL pageList = NIL DO FreeOne[]; ENDLOOP;
    ReleaseList[];
    END;

  GrabList: ENTRY PROCEDURE =
    -- acquires exclusive access to the storage list.
    BEGIN
    UNTIL listState = stable DO WAIT listStable; ENDLOOP;
    listState ← unstable;
    END;

  ReleaseList: ENTRY PROCEDURE =
    -- relinquishes exclusive access to the storage list.
    {listState ← stable; NOTIFY listStable};

  FlushList: AllocDefs.SwappingProcedure =
    -- flushes the list of free pages to try to help the swapper out.
    BEGIN
    IF AllocDefs.TryCodeSwapping[needed, info, seg] THEN RETURN[TRUE];
    GrabList[];
    DO
      IF pageList = NIL THEN
	BEGIN
	ReleaseList[];
	IF ~VMSpecial.PruneCache[needed] THEN RETURN[FALSE];
	GrabList[];
	END
      ELSE EXIT;
      ENDLOOP;
    IF needed = 1 THEN FreeOne[] ELSE UNTIL pageList = NIL DO FreeOne[]; ENDLOOP;
    ReleaseList[];
    RETURN[TRUE]
    END;

  FreeOne: PROCEDURE =
    -- returns the page at the head of the pageList to the Mesa allocator.
    BEGIN OPEN SegmentDefs;
    p: PagePointer = pageList.link;
    DeleteDataSegment[VMtoDataSegment[pageList]];
    pageList ← p;
    END;

  END.